Last active
June 29, 2020 11:39
-
-
Save VanTanev/8a6ea41257eba917241cb026e5f52240 to your computer and use it in GitHub Desktop.
Extract static type strings from io-ts definitions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as t from 'io-ts' | |
import * as gen from 'io-ts-codegen' | |
import { NullableType } from 'codecs/util/nullable' | |
export function stringifyNaive(identifier: string, codec: t.Any): string { | |
return `type ${identifier} = ${codec.name}` | |
} | |
export function stringifyFull(identifier: string, codec: t.Any): string { | |
return `type ${identifier} = ${gen.printStatic(getType(codec))}` | |
} | |
function getType(codec: t.Any): gen.TypeReference { | |
switch ((codec as any)._tag) { | |
case 'StringType': | |
return gen.stringType | |
case 'NumberType': | |
return gen.numberType | |
case 'BooleanType': | |
return gen.booleanType | |
case 'NullType': | |
return gen.nullType | |
case 'UndefinedType': | |
return gen.undefinedType | |
case 'UnknownType': | |
return gen.unknownType | |
case 'InterfaceType': | |
return generateInterfaceType(codec as any) | |
case 'ArrayType': | |
return generateArrayType(codec as any) | |
case 'NullableType': | |
return generateNullableType(codec as any) | |
case 'KeyofType': | |
return generateKeyofType(codec as any) | |
case 'ExactType': | |
return generateInterfaceType((codec as any).type) | |
case 'UnionType': | |
return generateUnionType(codec as any) | |
} | |
throw Error(`Cannot extract type for ${codec.name}: ${(codec as any)._tag}`) | |
} | |
function generateInterfaceType(codec: t.TypeC<t.Props>): gen.TypeReference { | |
return gen.typeCombinator( | |
Object.keys(codec.props).map(key => gen.property(key, getType(codec.props[key]))), | |
) | |
} | |
function generateArrayType(codec: t.ArrayType<t.Any>): gen.TypeReference { | |
return gen.arrayCombinator(getType(codec.type)) | |
} | |
function generateKeyofType(codec: t.KeyofType<any>): gen.TypeReference { | |
return gen.keyofCombinator(Object.keys(codec.keys)) | |
} | |
function generateNullableType(codec: NullableType<any>): gen.TypeReference { | |
return gen.unionCombinator([getType(codec.type), gen.nullType]) | |
} | |
function generateUnionType(codec: t.UnionType<any, any, any>): gen.TypeReference { | |
return gen.unionCombinator(codec.types.map(getType)) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as FS from 'fs' | |
import * as Path from 'path' | |
import * as t from 'io-ts' | |
import * as prettier from 'prettier' | |
import * as rimraf from 'rimraf' | |
import * as ts from './io-ts-stringify' | |
import { CastMemberCodec } from 'codecs/CastMember' | |
import { CharacterCodec, CharacterWithPortrayalCodec } from 'codecs/Character' | |
import { RelationshipCodec } from 'codecs/Relationship' | |
import { SeasonCodec } from 'codecs/Season' | |
import { EpisodeCodec } from 'codecs/Episode' | |
import { FeatureCodec } from 'codecs/Feature' | |
import { SeriesCodec } from 'codecs/Series' | |
import { FeatureStorylineCodec } from 'codecs/storyline/FeatureStoryline' | |
import { EpisodeStorylineCodec } from 'codecs/storyline/EpisodeStoryline' | |
import { StoryRoleCodec } from 'codecs/storyline/StoryRole' | |
import { SearchResultCodec } from 'codecs/SearchResult' | |
import { LockCodec } from 'codecs/Lock' | |
const ARGV = process.argv.slice(2) | |
const OUTPUT_DIR = ARGV[0] ?? Path.join(process.cwd(), 'docs', 'types') | |
ensureEnv() | |
writeDocfile( | |
'README.md', | |
`# Docs | |
### [Main Types](./main_types.md) | |
These contain the main title types used in the system | |
### [Component Types](./component_types.md) | |
These are named types that are used inside main types. Things like Storyline, for example. | |
### [API Types](./api_types.md) | |
These are API specific types that do not strictly depend on the SPARQL database. | |
`, | |
) | |
writeTypesDocfile('main_types.md', [ | |
['Feature', FeatureCodec], | |
['Series', SeriesCodec], | |
['Episode', EpisodeCodec], | |
['Season', SeasonCodec], | |
]) | |
writeTypesDocfile('component_types.md', [ | |
['FeatureStoryline', FeatureStorylineCodec, 'Feature Storyline'], | |
['EpisodeStoryline', EpisodeStorylineCodec, 'Episode Storyline'], | |
['StoryRole', StoryRoleCodec, 'Story Role'], | |
['Character', CharacterCodec], | |
['CharacterWithPortrayal', CharacterWithPortrayalCodec, 'Character With Portrayal'], | |
['CastMember', CastMemberCodec, 'Cast Member'], | |
['Relationship', RelationshipCodec], | |
]) | |
writeTypesDocfile('api_types.md', [ | |
['SearchResult', SearchResultCodec, 'Search Result', ts.stringifyFull], | |
['Lock', LockCodec], | |
]) | |
//////////////////////////////////////// | |
function writeTypesDocfile( | |
filename: string, | |
defs: Array<[string, t.Any, string?, ((identifer: string, codec: t.Any) => string)?]>, | |
): void { | |
writeDocfile( | |
filename, | |
defs | |
.map(([identifier, codec, label, stringify = ts.stringifyNaive]) => { | |
let definition = stringify(identifier, codec) | |
let formatted = prettier.format(definition, { | |
parser: 'babel-ts', | |
semi: false, | |
tabWidth: 4, | |
}) | |
let markdown = [ | |
`# ${label ?? identifier}`, | |
'', | |
'```typescript', | |
formatted, | |
'```', | |
].join('\n') | |
return markdown | |
}) | |
.join('\n\n---\n\n'), | |
) | |
} | |
function writeDocfile(filename: string, contents: string): void { | |
FS.writeFileSync(Path.join(OUTPUT_DIR, filename), contents) | |
} | |
function ensureEnv(): void { | |
clearOutputDir() | |
createOutputDir() | |
} | |
function createOutputDir(): void { | |
FS.mkdirSync(OUTPUT_DIR, { recursive: true }) | |
} | |
function clearOutputDir(): void { | |
rimraf.sync(OUTPUT_DIR) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment