Skip to content

Commit

Permalink
feat: Split files by type
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin-greene-ck committed Apr 10, 2019
1 parent faaff72 commit c15a838
Show file tree
Hide file tree
Showing 186 changed files with 23,381 additions and 24,368 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,20 +486,20 @@ type MyUnion = IMyUnionWithOption1 | IMyUnionWithOption2
interface IMyUnionWithOption1 {
__type: MyUnionType.MyUnionWithOption1
option1: string
option2?: void
option2?: undefined
}
interface IMyUnionWithOption2 {
__type: MyUnionType.MyUnionWithOption2
option1?: void
option1?: undefined
option2: number
}
type MyUnionArgs = IMyUnionWithOption1Args | IMyUnionWithOption2Args
interface IMyUnionWithOption1Args {
option1: string
option2?: void
option2?: undefined
}
interface IMyUnionWithOption2Args {
option1?: void
option1?: undefined
option2: number
}
```
Expand All @@ -524,7 +524,7 @@ function processUnion(union: MyUnion) {
}
```

The fact that each interface we generate defines one required field and some n number of optional `void` fields we can do things like check `union.option2 !== undefined` without a compiler error, but we will get a compiler error if you try to use a value that shouldn't exist on a given union. This expands the ways you can operate on unions to be more general.
The fact that each interface we generate defines one required field and some n number of optional `undefined` fields we can do things like check `union.option2 !== undefined` without a compiler error, but we will get a compiler error if you try to use a value that shouldn't exist on a given union. This expands the ways you can operate on unions to be more general.

Using this form will require that you prove to the compiler that one (and only one) field is set for your unions.

Expand Down
509 changes: 186 additions & 323 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
],
"scripts": {
"clean": "rimraf ./coverage ./dist ./**/codegen",
"clean-all": "npm run clean && rimraf ./node_modules package-lock.json",
"clean:all": "npm run clean && rimraf ./node_modules package-lock.json",
"codegen": "node ./dist/main/bin/index.js --sourceDir ./src/tests/integration/thrift --outDir ./src/tests/integration/apache/codegen",
"prebuild": "npm run clean && npm run lint && npm run format",
"build": "npm run build:only",
Expand Down
17 changes: 9 additions & 8 deletions src/main/debugger.ts → src/main/debugger/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { TextLocation } from '@creditkarma/thrift-parser'
import * as os from 'os'

import { ErrorType, IResolvedFile, IThriftError } from './types'
import { ErrorType, IThriftError } from '../errors'
import { IProcessedFile } from '../types'

interface IFormattedError {
sourceLine: string
Expand Down Expand Up @@ -46,8 +47,8 @@ function errorType(type: ErrorType): string {
}
}

function printErrorForFile(file: IResolvedFile): void {
const sourceLines: Array<string> = file.source.split(os.EOL)
function printErrorForFile<T extends IProcessedFile>(file: T): void {
const sourceLines: Array<string> = file.sourceFile.source.split(os.EOL)
const formattedErrors: Array<IFormattedError> = file.errors.map(
(next: IThriftError): IFormattedError => {
return formatError(next)
Expand All @@ -70,9 +71,9 @@ function printErrorForFile(file: IResolvedFile): void {
}

console.log(
`Error generating file '${file.path}/${file.name}.thrift': ${
file.errors.length
} errors found:`,
`Error generating file '${file.sourceFile.path}/${
file.sourceFile.name
}.thrift': ${file.errors.length} errors found:`,
)
formattedErrors.forEach(
(err: IFormattedError): void => {
Expand All @@ -89,9 +90,9 @@ function printErrorForFile(file: IResolvedFile): void {
)
}

export function printErrors(files: Array<IResolvedFile>): void {
export function printErrors<T extends IProcessedFile>(files: Array<T>): void {
files.forEach(
(next: IResolvedFile): void => {
(next: T): void => {
printErrorForFile(next)
},
)
Expand Down
35 changes: 35 additions & 0 deletions src/main/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { TextLocation } from '@creditkarma/thrift-parser'
import { emptyLocation } from './utils'

export const enum ErrorType {
ValidationError = 'ValidationError',
ResolutionError = 'ResolutionError',
GenerationError = 'GenerationError',
}

export interface IThriftError {
type: ErrorType
message: string
loc: TextLocation
}

export class ValidationError extends Error {
public message: string
public loc: TextLocation
constructor(msg: string, loc: TextLocation) {
super(msg)
this.message = msg
this.loc = loc
}
}

export function createValidationError(
message: string,
loc: TextLocation = emptyLocation(),
): IThriftError {
return {
type: ErrorType.ValidationError,
message,
loc,
}
}
192 changes: 162 additions & 30 deletions src/main/generator/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import * as ts from 'typescript'

import {
IIdentifierMap,
IMakeOptions,
INamespaceFile,
IRenderedCache,
IRenderedFile,
ExceptionDefinition,
ServiceDefinition,
StructDefinition,
ThriftStatement,
UnionDefinition,
} from '@creditkarma/thrift-parser'

import { rendererForTarget } from '../render'
import { processStatements, renderStatement } from './iterator'

import { exportsForFile } from '../resolver/utils'
import {
IGeneratedFile,
INamespace,
IRenderer,
IRenderState,
IThriftProject,
} from '../types'

import { processStatements } from './iterator'

/**
* Export this directly is useful for generating code without generating files
*/
Expand All @@ -30,31 +38,155 @@ export { processStatements } from './iterator'
*/
export function generateFile(
renderer: IRenderer,
resolvedFile: INamespaceFile,
options: IMakeOptions,
cache: IRenderedCache = {},
): IRenderedFile {
const cacheKey: string = resolvedFile.namespace.path

if (cacheKey === '/' || cache[cacheKey] === undefined) {
const identifiers: IIdentifierMap = resolvedFile.identifiers
const state: IRenderState = { options, identifiers }
const statements: Array<ts.Statement> = [
...renderer.renderIncludes(
resolvedFile.namespace.path,
resolvedFile,
options,
),
...processStatements(resolvedFile.body, state, renderer),
]
statements: Array<ThriftStatement>,
state: IRenderState,
): Array<ts.Statement> {
return processStatements(statements, state, renderer)
}

function generateFileFromStatements(
statements: Array<
| ServiceDefinition
| ExceptionDefinition
| UnionDefinition
| StructDefinition
>,
namespace: INamespace,
thriftProject: IThriftProject,
renderer: IRenderer,
): Array<IGeneratedFile> {
const result: Array<IGeneratedFile> = []

statements.forEach(
(
statement:
| ServiceDefinition
| ExceptionDefinition
| UnionDefinition
| StructDefinition,
) => {
const state: IRenderState = {
options: thriftProject.options,
currentNamespace: namespace,
currentDefinitions: exportsForFile([statement]),
project: thriftProject,
}

const structFile: IGeneratedFile = {
type: 'GeneratedFile',
name: statement.name.value,
path: namespace.namespace.path,
body: renderStatement(statement, state, renderer),
}

structFile.body = [
...renderer.renderImports([statement], state),
...structFile.body,
]

result.push(structFile)
},
)

return result
}

function generateFilesFromKey(
key: 'constants' | 'typedefs',
namespace: INamespace,
thriftProject: IThriftProject,
renderer: IRenderer,
): Array<IGeneratedFile> {
const result: Array<IGeneratedFile> = []
const statements: Array<ThriftStatement> = namespace[key]

cache[cacheKey] = {
outPath: resolvedFile.namespace.path,
namespace: resolvedFile.namespace,
statements,
identifiers,
if (statements.length > 0) {
const constantsFile: IGeneratedFile = {
type: 'GeneratedFile',
name: key,
path: namespace.namespace.path,
body: [],
}

const state: IRenderState = {
options: thriftProject.options,
currentNamespace: namespace,
currentDefinitions: exportsForFile(statements),
project: thriftProject,
}

statements.forEach((statement: ThriftStatement) => {
constantsFile.body = [
...constantsFile.body,
...renderStatement(statement, state, renderer),
]
})

constantsFile.body = [
...renderer.renderImports(statements, state),
...constantsFile.body,
]

result.push(constantsFile)
}

return cache[cacheKey]
return result
}

export function generateProject(
thriftProject: IThriftProject,
): Array<IGeneratedFile> {
let result: Array<IGeneratedFile> = []
const renderer: IRenderer = rendererForTarget(thriftProject.options.target)

Object.keys(thriftProject.namespaces).forEach((namespaceName: string) => {
const namespace: INamespace = thriftProject.namespaces[namespaceName]

// Generate content for this namespace
result = result.concat(
generateFilesFromKey(
'constants',
namespace,
thriftProject,
renderer,
),
generateFilesFromKey(
'typedefs',
namespace,
thriftProject,
renderer,
),
generateFileFromStatements(
[
...namespace.structs,
...namespace.unions,
...namespace.exceptions,
],
namespace,
thriftProject,
renderer,
),
generateFileFromStatements(
namespace.services,
namespace,
thriftProject,
renderer,
),
)

// Index file for this namespace
result.push({
type: 'GeneratedFile',
name: 'index',
path: namespace.namespace.path,
body: renderer.renderIndex({
options: thriftProject.options,
currentNamespace: namespace,
currentDefinitions: {},
project: thriftProject,
}),
})
})

return result
}
Loading

0 comments on commit c15a838

Please sign in to comment.