Format typescript (#2082)

This commit is contained in:
David Siegel
2023-01-07 16:11:15 -08:00
committed by GitHub
parent 7599c476f8
commit c1f6db7206
47 changed files with 4143 additions and 6968 deletions

2737
data/lib.d.ts vendored
View File

@ -13,12 +13,8 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference no-default-lib="true"/>
/////////////////////////////
/// ECMAScript APIs
/////////////////////////////
@ -26,61 +22,22 @@ and limitations under the License.
declare const NaN: number;
declare const Infinity: number;
declare function eval(x: string): any;
declare function parseInt(s: string, radix?: number): number;
declare function parseFloat(string: string): number;
declare function isNaN(number: number): boolean;
declare function isFinite(number: number): boolean;
declare function decodeURI(encodedURI: string): string;
declare function decodeURIComponent(encodedURIComponent: string): string;
declare function encodeURI(uri: string): string;
declare function encodeURIComponent(uriComponent: string): string;
interface PropertyDescriptor {
@ -97,181 +54,70 @@ interface PropertyDescriptorMap {
}
interface Object {
constructor: Function;
toString(): string;
toLocaleString(): string;
valueOf(): Object;
hasOwnProperty(v: string): boolean;
isPrototypeOf(v: Object): boolean;
propertyIsEnumerable(v: string): boolean;
}
interface ObjectConstructor {
new(value?: any): Object;
new (value?: any): Object;
(): any;
(value: any): any;
readonly prototype: Object;
getPrototypeOf(o: any): any;
getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor | undefined;
getOwnPropertyNames(o: any): string[];
create(o: object | null): any;
create(o: object | null, properties: PropertyDescriptorMap & ThisType<any>): any;
defineProperty(o: any, p: string, attributes: PropertyDescriptor & ThisType<any>): any;
defineProperties(o: any, properties: PropertyDescriptorMap & ThisType<any>): any;
seal<T>(o: T): T;
freeze<T>(a: T[]): ReadonlyArray<T>;
freeze<T extends Function>(f: T): T;
freeze<T>(o: T): Readonly<T>;
preventExtensions<T>(o: T): T;
isSealed(o: any): boolean;
isFrozen(o: any): boolean;
isExtensible(o: any): boolean;
keys(o: {}): string[];
}
declare const Object: ObjectConstructor;
interface Function {
apply(this: Function, thisArg: any, argArray?: any): any;
call(this: Function, thisArg: any, ...argArray: any[]): any;
bind(this: Function, thisArg: any, ...argArray: any[]): any;
toString(): string;
prototype: any;
@ -283,11 +129,7 @@ interface Function {
}
interface FunctionConstructor {
new(...args: string[]): Function;
new (...args: string[]): Function;
(...args: string[]): Function;
readonly prototype: Function;
}
@ -301,147 +143,70 @@ interface IArguments {
}
interface String {
toString(): string;
charAt(pos: number): string;
charCodeAt(index: number): number;
concat(...strings: string[]): string;
indexOf(searchString: string, position?: number): number;
lastIndexOf(searchString: string, position?: number): number;
localeCompare(that: string): number;
match(regexp: string | RegExp): RegExpMatchArray | null;
replace(searchValue: string | RegExp, replaceValue: string): string;
replace(searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string;
search(regexp: string | RegExp): number;
slice(start?: number, end?: number): string;
split(separator: string | RegExp, limit?: number): string[];
substring(start: number, end?: number): string;
toLowerCase(): string;
toLocaleLowerCase(): string;
toUpperCase(): string;
toLocaleUpperCase(): string;
trim(): string;
readonly length: number;
// IE extensions
substr(from: number, length?: number): string;
valueOf(): string;
readonly [index: number]: string;
}
interface StringConstructor {
new(value?: any): String;
new (value?: any): String;
(value?: any): string;
readonly prototype: String;
fromCharCode(...codes: number[]): string;
}
declare const String: StringConstructor;
interface Boolean {
valueOf(): boolean;
}
interface BooleanConstructor {
new(value?: any): Boolean;
new (value?: any): Boolean;
(value?: any): boolean;
readonly prototype: Boolean;
}
@ -449,65 +214,33 @@ interface BooleanConstructor {
declare const Boolean: BooleanConstructor;
interface Number {
toString(radix?: number): string;
toFixed(fractionDigits?: number): string;
toExponential(fractionDigits?: number): string;
toPrecision(precision?: number): string;
valueOf(): number;
}
interface NumberConstructor {
new(value?: any): Number;
new (value?: any): Number;
(value?: any): number;
readonly prototype: Number;
readonly MAX_VALUE: number;
readonly MIN_VALUE: number;
readonly NaN: number;
readonly NEGATIVE_INFINITY: number;
readonly POSITIVE_INFINITY: number;
}
declare const Number: NumberConstructor;
interface TemplateStringsArray extends ReadonlyArray<string> {
@ -515,285 +248,176 @@ interface TemplateStringsArray extends ReadonlyArray<string> {
}
interface Math {
readonly E: number;
readonly LN10: number;
readonly LN2: number;
readonly LOG2E: number;
readonly LOG10E: number;
readonly PI: number;
readonly SQRT1_2: number;
readonly SQRT2: number;
abs(x: number): number;
acos(x: number): number;
asin(x: number): number;
atan(x: number): number;
atan2(y: number, x: number): number;
ceil(x: number): number;
cos(x: number): number;
exp(x: number): number;
floor(x: number): number;
log(x: number): number;
max(...values: number[]): number;
min(...values: number[]): number;
pow(x: number, y: number): number;
random(): number;
round(x: number): number;
sin(x: number): number;
sqrt(x: number): number;
tan(x: number): number;
}
declare const Math: Math;
interface Date {
toString(): string;
toDateString(): string;
toTimeString(): string;
toLocaleString(): string;
toLocaleDateString(): string;
toLocaleTimeString(): string;
valueOf(): number;
getTime(): number;
getFullYear(): number;
getUTCFullYear(): number;
getMonth(): number;
getUTCMonth(): number;
getDate(): number;
getUTCDate(): number;
getDay(): number;
getUTCDay(): number;
getHours(): number;
getUTCHours(): number;
getMinutes(): number;
getUTCMinutes(): number;
getSeconds(): number;
getUTCSeconds(): number;
getMilliseconds(): number;
getUTCMilliseconds(): number;
getTimezoneOffset(): number;
setTime(time: number): number;
setMilliseconds(ms: number): number;
setUTCMilliseconds(ms: number): number;
setSeconds(sec: number, ms?: number): number;
setUTCSeconds(sec: number, ms?: number): number;
setMinutes(min: number, sec?: number, ms?: number): number;
setUTCMinutes(min: number, sec?: number, ms?: number): number;
setHours(hours: number, min?: number, sec?: number, ms?: number): number;
setUTCHours(hours: number, min?: number, sec?: number, ms?: number): number;
setDate(date: number): number;
setUTCDate(date: number): number;
setMonth(month: number, date?: number): number;
setUTCMonth(month: number, date?: number): number;
setFullYear(year: number, month?: number, date?: number): number;
setUTCFullYear(year: number, month?: number, date?: number): number;
toUTCString(): string;
toISOString(): string;
toJSON(key?: any): string;
}
interface DateConstructor {
new(): Date;
new(value: number): Date;
new(value: string): Date;
new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
new (): Date;
new (value: number): Date;
new (value: string): Date;
new (
year: number,
month: number,
date?: number,
hours?: number,
minutes?: number,
seconds?: number,
ms?: number
): Date;
(): string;
readonly prototype: Date;
parse(s: string): number;
UTC(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): number;
UTC(
year: number,
month: number,
date?: number,
hours?: number,
minutes?: number,
seconds?: number,
ms?: number
): number;
now(): number;
}
@ -810,28 +434,16 @@ interface RegExpExecArray extends Array<string> {
}
interface RegExp {
exec(string: string): RegExpExecArray | null;
test(string: string): boolean;
readonly source: string;
readonly global: boolean;
readonly ignoreCase: boolean;
readonly multiline: boolean;
lastIndex: number;
@ -841,8 +453,8 @@ interface RegExp {
}
interface RegExpConstructor {
new(pattern: RegExp | string): RegExp;
new(pattern: string, flags?: string): RegExp;
new (pattern: RegExp | string): RegExp;
new (pattern: string, flags?: string): RegExp;
(pattern: RegExp | string): RegExp;
(pattern: string, flags?: string): RegExp;
readonly prototype: RegExp;
@ -869,73 +481,67 @@ interface Error {
}
interface ErrorConstructor {
new(message?: string): Error;
new (message?: string): Error;
(message?: string): Error;
readonly prototype: Error;
}
declare const Error: ErrorConstructor;
interface EvalError extends Error {
}
interface EvalError extends Error {}
interface EvalErrorConstructor {
new(message?: string): EvalError;
new (message?: string): EvalError;
(message?: string): EvalError;
readonly prototype: EvalError;
}
declare const EvalError: EvalErrorConstructor;
interface RangeError extends Error {
}
interface RangeError extends Error {}
interface RangeErrorConstructor {
new(message?: string): RangeError;
new (message?: string): RangeError;
(message?: string): RangeError;
readonly prototype: RangeError;
}
declare const RangeError: RangeErrorConstructor;
interface ReferenceError extends Error {
}
interface ReferenceError extends Error {}
interface ReferenceErrorConstructor {
new(message?: string): ReferenceError;
new (message?: string): ReferenceError;
(message?: string): ReferenceError;
readonly prototype: ReferenceError;
}
declare const ReferenceError: ReferenceErrorConstructor;
interface SyntaxError extends Error {
}
interface SyntaxError extends Error {}
interface SyntaxErrorConstructor {
new(message?: string): SyntaxError;
new (message?: string): SyntaxError;
(message?: string): SyntaxError;
readonly prototype: SyntaxError;
}
declare const SyntaxError: SyntaxErrorConstructor;
interface TypeError extends Error {
}
interface TypeError extends Error {}
interface TypeErrorConstructor {
new(message?: string): TypeError;
new (message?: string): TypeError;
(message?: string): TypeError;
readonly prototype: TypeError;
}
declare const TypeError: TypeErrorConstructor;
interface URIError extends Error {
}
interface URIError extends Error {}
interface URIErrorConstructor {
new(message?: string): URIError;
new (message?: string): URIError;
(message?: string): URIError;
readonly prototype: URIError;
}
@ -943,305 +549,149 @@ interface URIErrorConstructor {
declare const URIError: URIErrorConstructor;
interface JSON {
parse(text: string, reviver?: (key: any, value: any) => any): any;
stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): string;
stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
}
declare const JSON: JSON;
/////////////////////////////
/// ECMAScript Array API (specially handled by compiler)
/////////////////////////////
interface ReadonlyArray<T> {
readonly length: number;
toString(): string;
toLocaleString(): string;
concat(...items: ReadonlyArray<T>[]): T[];
concat(...items: (T | ReadonlyArray<T>)[]): T[];
join(separator?: string): string;
slice(start?: number, end?: number): T[];
indexOf(searchElement: T, fromIndex?: number): number;
lastIndexOf(searchElement: T, fromIndex?: number): number;
every(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => boolean, thisArg?: any): boolean;
some(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => boolean, thisArg?: any): boolean;
forEach(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => void, thisArg?: any): void;
map<U>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => U, thisArg?: any): U[];
filter<S extends T>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => value is S, thisArg?: any): S[];
filter<S extends T>(
callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => value is S,
thisArg?: any
): S[];
filter(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => any, thisArg?: any): T[];
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T, initialValue: T): T;
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => U, initialValue: U): U;
reduce(
callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T,
initialValue: T
): T;
reduce<U>(
callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => U,
initialValue: U
): U;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T, initialValue: T): T;
reduceRight(
callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T,
initialValue: T
): T;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => U, initialValue: U): U;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => U,
initialValue: U
): U;
readonly [n: number]: T;
}
interface Array<T> {
length: number;
toString(): string;
toLocaleString(): string;
push(...items: T[]): number;
pop(): T | undefined;
concat(...items: ReadonlyArray<T>[]): T[];
concat(...items: (T | ReadonlyArray<T>)[]): T[];
join(separator?: string): string;
reverse(): T[];
shift(): T | undefined;
slice(start?: number, end?: number): T[];
sort(compareFn?: (a: T, b: T) => number): this;
splice(start: number, deleteCount?: number): T[];
splice(start: number, deleteCount: number, ...items: T[]): T[];
unshift(...items: T[]): number;
indexOf(searchElement: T, fromIndex?: number): number;
lastIndexOf(searchElement: T, fromIndex?: number): number;
every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[];
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
reduce<U>(
callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U,
initialValue: U
): U;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
reduceRight(
callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
initialValue: T
): T;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U,
initialValue: U
): U;
[n: number]: T;
}
interface ArrayConstructor {
new(arrayLength?: number): any[];
new (arrayLength?: number): any[];
new <T>(arrayLength: number): T[];
new <T>(...items: T[]): T[];
(arrayLength?: number): any[];
@ -1264,39 +714,33 @@ interface TypedPropertyDescriptor<T> {
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type MethodDecorator = <T>(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) => PromiseLike<T>;
declare type PromiseConstructorLike = new <T>(
executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void
) => PromiseLike<T>;
interface PromiseLike<T> {
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): PromiseLike<TResult1 | TResult2>;
then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
): PromiseLike<TResult1 | TResult2>;
}
interface Promise<T> {
then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
): Promise<TResult1 | TResult2>;
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
): Promise<T | TResult>;
}
interface ArrayLike<T> {
@ -1304,60 +748,30 @@ interface ArrayLike<T> {
readonly [n: number]: T;
}
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Record<K extends string, T> = {
[P in K]: T;
};
interface ThisType<T> { }
interface ThisType<T> {}
interface ArrayBuffer {
readonly byteLength: number;
slice(begin: number, end?: number): ArrayBuffer;
}
interface ArrayBufferTypes {
ArrayBuffer: ArrayBuffer;
}
@ -1365,25 +779,16 @@ type ArrayBufferLike = ArrayBufferTypes[keyof ArrayBufferTypes];
interface ArrayBufferConstructor {
readonly prototype: ArrayBuffer;
new(byteLength: number): ArrayBuffer;
new (byteLength: number): ArrayBuffer;
isView(arg: any): arg is ArrayBufferView;
}
declare const ArrayBuffer: ArrayBufferConstructor;
interface ArrayBufferView {
buffer: ArrayBufferLike;
byteLength: number;
byteOffset: number;
}
@ -1391,576 +796,209 @@ interface DataView {
readonly buffer: ArrayBuffer;
readonly byteLength: number;
readonly byteOffset: number;
getFloat32(byteOffset: number, littleEndian?: boolean): number;
getFloat64(byteOffset: number, littleEndian?: boolean): number;
getInt8(byteOffset: number): number;
getInt16(byteOffset: number, littleEndian?: boolean): number;
getInt32(byteOffset: number, littleEndian?: boolean): number;
getUint8(byteOffset: number): number;
getUint16(byteOffset: number, littleEndian?: boolean): number;
getUint32(byteOffset: number, littleEndian?: boolean): number;
setFloat32(byteOffset: number, value: number, littleEndian?: boolean): void;
setFloat64(byteOffset: number, value: number, littleEndian?: boolean): void;
setInt8(byteOffset: number, value: number): void;
setInt16(byteOffset: number, value: number, littleEndian?: boolean): void;
setInt32(byteOffset: number, value: number, littleEndian?: boolean): void;
setUint8(byteOffset: number, value: number): void;
setUint16(byteOffset: number, value: number, littleEndian?: boolean): void;
setUint32(byteOffset: number, value: number, littleEndian?: boolean): void;
}
interface DataViewConstructor {
new(buffer: ArrayBufferLike, byteOffset?: number, byteLength?: number): DataView;
new (buffer: ArrayBufferLike, byteOffset?: number, byteLength?: number): DataView;
}
declare const DataView: DataViewConstructor;
interface Int8Array {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Int8Array) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Int8Array) => any, thisArg?: any): Int8Array;
find(predicate: (value: number, index: number, obj: Int8Array) => boolean, thisArg?: any): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Int8Array) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Int8Array) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(callbackfn: (value: number, index: number, array: Int8Array) => number, thisArg?: any): Int8Array;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number
): number;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number,
initialValue: number
): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue: number): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int8Array) => U,
initialValue: U
): U;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int8Array) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int8Array) => U, initialValue: U): U;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number
): number;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number,
initialValue: number
): number;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int8Array) => U,
initialValue: U
): U;
reverse(): Int8Array;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Int8Array;
some(callbackfn: (value: number, index: number, array: Int8Array) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Int8Array;
toLocaleString(): string;
toString(): string;
[index: number]: number;
}
interface Int8ArrayConstructor {
readonly prototype: Int8Array;
new(length: number): Int8Array;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int8Array;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Int8Array;
new (length: number): Int8Array;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int8Array;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Int8Array;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Int8Array;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Int8Array;
}
declare const Int8Array: Int8ArrayConstructor;
interface Uint8Array {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Uint8Array) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Uint8Array) => any, thisArg?: any): Uint8Array;
find(predicate: (value: number, index: number, obj: Uint8Array) => boolean, thisArg?: any): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Uint8Array) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Uint8Array) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(callbackfn: (value: number, index: number, array: Uint8Array) => number, thisArg?: any): Uint8Array;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number
): number;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number,
initialValue: number
): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue: number): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8Array) => U,
initialValue: U
): U;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8Array) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8Array) => U, initialValue: U): U;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number
): number;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number,
initialValue: number
): number;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8Array) => U,
initialValue: U
): U;
reverse(): Uint8Array;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Uint8Array;
some(callbackfn: (value: number, index: number, array: Uint8Array) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Uint8Array;
toLocaleString(): string;
toString(): string;
[index: number]: number;
@ -1968,236 +1006,120 @@ interface Uint8Array {
interface Uint8ArrayConstructor {
readonly prototype: Uint8Array;
new(length: number): Uint8Array;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint8Array;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint8Array;
new (length: number): Uint8Array;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint8Array;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint8Array;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Uint8Array;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint8Array;
}
declare const Uint8Array: Uint8ArrayConstructor;
interface Uint8ClampedArray {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => any, thisArg?: any): Uint8ClampedArray;
find(predicate: (value: number, index: number, obj: Uint8ClampedArray) => boolean, thisArg?: any): number | undefined;
filter(
callbackfn: (value: number, index: number, array: Uint8ClampedArray) => any,
thisArg?: any
): Uint8ClampedArray;
find(
predicate: (value: number, index: number, obj: Uint8ClampedArray) => boolean,
thisArg?: any
): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Uint8ClampedArray) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(
callbackfn: (value: number, index: number, array: Uint8ClampedArray) => number,
thisArg?: any
): Uint8ClampedArray;
reduce(
callbackfn: (
previousValue: number,
currentValue: number,
currentIndex: number,
array: Uint8ClampedArray
) => number
): number;
reduce(
callbackfn: (
previousValue: number,
currentValue: number,
currentIndex: number,
array: Uint8ClampedArray
) => number,
initialValue: number
): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => U,
initialValue: U
): U;
reduceRight(
callbackfn: (
previousValue: number,
currentValue: number,
currentIndex: number,
array: Uint8ClampedArray
) => number
): number;
reduceRight(
callbackfn: (
previousValue: number,
currentValue: number,
currentIndex: number,
array: Uint8ClampedArray
) => number,
initialValue: number
): number;
map(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => number, thisArg?: any): Uint8ClampedArray;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number, initialValue: number): number;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => U, initialValue: U): U;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => U,
initialValue: U
): U;
reverse(): Uint8ClampedArray;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Uint8ClampedArray;
some(callbackfn: (value: number, index: number, array: Uint8ClampedArray) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Uint8ClampedArray;
toLocaleString(): string;
toString(): string;
[index: number]: number;
@ -2205,234 +1127,91 @@ interface Uint8ClampedArray {
interface Uint8ClampedArrayConstructor {
readonly prototype: Uint8ClampedArray;
new(length: number): Uint8ClampedArray;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint8ClampedArray;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint8ClampedArray;
new (length: number): Uint8ClampedArray;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint8ClampedArray;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint8ClampedArray;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Uint8ClampedArray;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint8ClampedArray;
}
declare const Uint8ClampedArray: Uint8ClampedArrayConstructor;
interface Int16Array {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Int16Array) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Int16Array) => any, thisArg?: any): Int16Array;
find(predicate: (value: number, index: number, obj: Int16Array) => boolean, thisArg?: any): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Int16Array) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Int16Array) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(callbackfn: (value: number, index: number, array: Int16Array) => number, thisArg?: any): Int16Array;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number
): number;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number,
initialValue: number
): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number, initialValue: number): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int16Array) => U,
initialValue: U
): U;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int16Array) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int16Array) => U, initialValue: U): U;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number
): number;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number,
initialValue: number
): number;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int16Array) => U,
initialValue: U
): U;
reverse(): Int16Array;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Int16Array;
some(callbackfn: (value: number, index: number, array: Int16Array) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Int16Array;
toLocaleString(): string;
toString(): string;
[index: number]: number;
@ -2440,237 +1219,91 @@ interface Int16Array {
interface Int16ArrayConstructor {
readonly prototype: Int16Array;
new(length: number): Int16Array;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int16Array;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Int16Array;
new (length: number): Int16Array;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int16Array;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Int16Array;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Int16Array;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Int16Array;
}
declare const Int16Array: Int16ArrayConstructor;
interface Uint16Array {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Uint16Array) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Uint16Array) => any, thisArg?: any): Uint16Array;
find(predicate: (value: number, index: number, obj: Uint16Array) => boolean, thisArg?: any): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Uint16Array) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Uint16Array) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(callbackfn: (value: number, index: number, array: Uint16Array) => number, thisArg?: any): Uint16Array;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number
): number;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number,
initialValue: number
): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number, initialValue: number): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint16Array) => U,
initialValue: U
): U;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint16Array) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint16Array) => U, initialValue: U): U;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number
): number;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number,
initialValue: number
): number;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint16Array) => U,
initialValue: U
): U;
reverse(): Uint16Array;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Uint16Array;
some(callbackfn: (value: number, index: number, array: Uint16Array) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Uint16Array;
toLocaleString(): string;
toString(): string;
[index: number]: number;
@ -2678,236 +1311,91 @@ interface Uint16Array {
interface Uint16ArrayConstructor {
readonly prototype: Uint16Array;
new(length: number): Uint16Array;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint16Array;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint16Array;
new (length: number): Uint16Array;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint16Array;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint16Array;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Uint16Array;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint16Array;
}
declare const Uint16Array: Uint16ArrayConstructor;
interface Int32Array {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Int32Array) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Int32Array) => any, thisArg?: any): Int32Array;
find(predicate: (value: number, index: number, obj: Int32Array) => boolean, thisArg?: any): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Int32Array) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Int32Array) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(callbackfn: (value: number, index: number, array: Int32Array) => number, thisArg?: any): Int32Array;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number
): number;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number,
initialValue: number
): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number, initialValue: number): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int32Array) => U,
initialValue: U
): U;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int32Array) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int32Array) => U, initialValue: U): U;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number
): number;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number,
initialValue: number
): number;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int32Array) => U,
initialValue: U
): U;
reverse(): Int32Array;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Int32Array;
some(callbackfn: (value: number, index: number, array: Int32Array) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Int32Array;
toLocaleString(): string;
toString(): string;
[index: number]: number;
@ -2915,235 +1403,91 @@ interface Int32Array {
interface Int32ArrayConstructor {
readonly prototype: Int32Array;
new(length: number): Int32Array;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int32Array;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Int32Array;
new (length: number): Int32Array;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Int32Array;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Int32Array;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Int32Array;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Int32Array;
}
declare const Int32Array: Int32ArrayConstructor;
interface Uint32Array {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Uint32Array) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Uint32Array) => any, thisArg?: any): Uint32Array;
find(predicate: (value: number, index: number, obj: Uint32Array) => boolean, thisArg?: any): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Uint32Array) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Uint32Array) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(callbackfn: (value: number, index: number, array: Uint32Array) => number, thisArg?: any): Uint32Array;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number
): number;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number,
initialValue: number
): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number, initialValue: number): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint32Array) => U,
initialValue: U
): U;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint32Array) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint32Array) => U, initialValue: U): U;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number
): number;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number,
initialValue: number
): number;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint32Array) => U,
initialValue: U
): U;
reverse(): Uint32Array;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Uint32Array;
some(callbackfn: (value: number, index: number, array: Uint32Array) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Uint32Array;
toLocaleString(): string;
toString(): string;
[index: number]: number;
@ -3151,236 +1495,91 @@ interface Uint32Array {
interface Uint32ArrayConstructor {
readonly prototype: Uint32Array;
new(length: number): Uint32Array;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint32Array;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint32Array;
new (length: number): Uint32Array;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Uint32Array;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Uint32Array;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Uint32Array;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Uint32Array;
}
declare const Uint32Array: Uint32ArrayConstructor;
interface Float32Array {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Float32Array) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Float32Array) => any, thisArg?: any): Float32Array;
find(predicate: (value: number, index: number, obj: Float32Array) => boolean, thisArg?: any): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Float32Array) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Float32Array) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(callbackfn: (value: number, index: number, array: Float32Array) => number, thisArg?: any): Float32Array;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number
): number;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number,
initialValue: number
): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number, initialValue: number): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float32Array) => U,
initialValue: U
): U;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float32Array) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float32Array) => U, initialValue: U): U;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number
): number;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number,
initialValue: number
): number;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float32Array) => U,
initialValue: U
): U;
reverse(): Float32Array;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Float32Array;
some(callbackfn: (value: number, index: number, array: Float32Array) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Float32Array;
toLocaleString(): string;
toString(): string;
[index: number]: number;
@ -3388,237 +1587,91 @@ interface Float32Array {
interface Float32ArrayConstructor {
readonly prototype: Float32Array;
new(length: number): Float32Array;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Float32Array;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Float32Array;
new (length: number): Float32Array;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Float32Array;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Float32Array;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Float32Array;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Float32Array;
}
declare const Float32Array: Float32ArrayConstructor;
interface Float64Array {
readonly BYTES_PER_ELEMENT: number;
readonly buffer: ArrayBufferLike;
readonly byteLength: number;
readonly byteOffset: number;
copyWithin(target: number, start: number, end?: number): this;
every(callbackfn: (value: number, index: number, array: Float64Array) => boolean, thisArg?: any): boolean;
fill(value: number, start?: number, end?: number): this;
filter(callbackfn: (value: number, index: number, array: Float64Array) => any, thisArg?: any): Float64Array;
find(predicate: (value: number, index: number, obj: Float64Array) => boolean, thisArg?: any): number | undefined;
findIndex(predicate: (value: number, index: number, obj: Float64Array) => boolean, thisArg?: any): number;
forEach(callbackfn: (value: number, index: number, array: Float64Array) => void, thisArg?: any): void;
indexOf(searchElement: number, fromIndex?: number): number;
join(separator?: string): string;
lastIndexOf(searchElement: number, fromIndex?: number): number;
readonly length: number;
map(callbackfn: (value: number, index: number, array: Float64Array) => number, thisArg?: any): Float64Array;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number
): number;
reduce(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number,
initialValue: number
): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number, initialValue: number): number;
reduce<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float64Array) => U,
initialValue: U
): U;
reduce<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float64Array) => U, initialValue: U): U;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number, initialValue: number): number;
reduceRight<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float64Array) => U, initialValue: U): U;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number
): number;
reduceRight(
callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number,
initialValue: number
): number;
reduceRight<U>(
callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Float64Array) => U,
initialValue: U
): U;
reverse(): Float64Array;
set(array: ArrayLike<number>, offset?: number): void;
slice(start?: number, end?: number): Float64Array;
some(callbackfn: (value: number, index: number, array: Float64Array) => boolean, thisArg?: any): boolean;
sort(compareFn?: (a: number, b: number) => number): this;
subarray(begin: number, end?: number): Float64Array;
toLocaleString(): string;
toString(): string;
[index: number]: number;
@ -3626,28 +1679,14 @@ interface Float64Array {
interface Float64ArrayConstructor {
readonly prototype: Float64Array;
new(length: number): Float64Array;
new(arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Float64Array;
new(buffer: ArrayBufferLike, byteOffset: number, length?: number): Float64Array;
new (length: number): Float64Array;
new (arrayOrArrayBuffer: ArrayLike<number> | ArrayBufferLike): Float64Array;
new (buffer: ArrayBufferLike, byteOffset: number, length?: number): Float64Array;
readonly BYTES_PER_ELEMENT: number;
of(...items: number[]): Float64Array;
from(arrayLike: ArrayLike<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): Float64Array;
}
declare const Float64Array: Float64ArrayConstructor;

View File

@ -12,7 +12,6 @@
"test": "script/test",
"start": "script/watch",
"pkg": "script/make-pkgs.sh",
"format": "prettier --write **/*.ts",
"tslint": "tslint --project src/cli --exclude 'src/__tests__/**' --exclude 'src/quicktype-core/input/io/get-stream/**'",
"clean": "rm -rf dist node_modules *~",
"publish": "script/publish.sh"

View File

@ -8,29 +8,29 @@ import * as shell from "shelljs";
import * as semver from "semver";
function exec(command: string) {
const result = shell.exec(command, { silent: true });
return (result.stdout as string).trim();
const result = shell.exec(command, { silent: true });
return (result.stdout as string).trim();
}
const PUBLISHED = (() => {
// Get the highest published version of any tag
const all = JSON.parse(exec(`npm show quicktype versions --json`));
return all[all.length - 1];
// Get the highest published version of any tag
const all = JSON.parse(exec(`npm show quicktype versions --json`));
return all[all.length - 1];
})();
const CURRENT = exec(`npm version`).match(/quicktype: '(.+)'/)![1];
switch (semver.compare(CURRENT, PUBLISHED)) {
case -1:
console.error(`* package.json version is ${CURRENT} but ${PUBLISHED} is published. Patching...`);
exec(`npm version ${PUBLISHED} --force --no-git-tag-version`);
shell.exec(`npm version patch --no-git-tag-version`);
break;
case 0:
console.error(`* package.json version is ${CURRENT} but ${PUBLISHED} is published. Patching...`);
shell.exec(`npm version patch --no-git-tag-version`);
break;
default:
// Greater than published, nothing to do
break;
case -1:
console.error(`* package.json version is ${CURRENT} but ${PUBLISHED} is published. Patching...`);
exec(`npm version ${PUBLISHED} --force --no-git-tag-version`);
shell.exec(`npm version patch --no-git-tag-version`);
break;
case 0:
console.error(`* package.json version is ${CURRENT} but ${PUBLISHED} is published. Patching...`);
shell.exec(`npm version patch --no-git-tag-version`);
break;
default:
// Greater than published, nothing to do
break;
}

View File

@ -341,14 +341,14 @@ function makeOptionDefinitions(targetLanguages: TargetLanguage[]): OptionDefinit
targetLanguages.length < 2
? []
: [
{
name: "lang",
alias: "l",
type: String,
typeLabel: makeLangTypeLabel(targetLanguages),
description: "The target language."
}
];
{
name: "lang",
alias: "l",
type: String,
typeLabel: makeLangTypeLabel(targetLanguages),
description: "The target language."
}
];
const afterLang: OptionDefinition[] = [
{
name: "src-lang",
@ -595,18 +595,13 @@ function parseOptions(definitions: OptionDefinition[], argv: string[], partial:
}
}
const options: { rendererOptions: RendererOptions;[key: string]: any } = { rendererOptions: {} };
const options: { rendererOptions: RendererOptions; [key: string]: any } = { rendererOptions: {} };
for (const o of definitions) {
if (!hasOwnProperty(opts, o.name)) continue;
const v = opts[o.name] as string;
if (o.renderer !== undefined) options.rendererOptions[o.name] = v;
else {
const k = _.lowerFirst(
o.name
.split("-")
.map(_.upperFirst)
.join("")
);
const k = _.lowerFirst(o.name.split("-").map(_.upperFirst).join(""));
options[k] = v;
}
}
@ -820,10 +815,9 @@ export async function makeQuicktypeOptions(
collectionFile
);
for (const src of postmanSources) {
sources.push(Object.assign(
{ kind: "json" },
stringSourceDataToStreamSourceData(src)
) as JSONTypeSource);
sources.push(
Object.assign({ kind: "json" }, stringSourceDataToStreamSourceData(src)) as JSONTypeSource
);
}
if (postmanSources.length > 1) {
fixedTopLevels = true;

File diff suppressed because one or more lines are too long

View File

@ -316,7 +316,10 @@ export function gatherNames(graph: TypeGraph, destructive: boolean, debugPrint:
alternatives = new Set();
}
alternatives = setUnion(alternatives, setMap(names, name => `${name}_${t.kind}`));
alternatives = setUnion(
alternatives,
setMap(names, name => `${name}_${t.kind}`)
);
directAlternativesForType.set(t, alternatives);
}

View File

@ -226,7 +226,11 @@ export class Graph<T> {
stronglyConnectedComponents(): Graph<ReadonlySet<T>> {
const components = stronglyConnectedComponents(this._successors);
const componentSuccessors = buildMetaSuccessors(this._successors, components);
return new Graph(components.map(ns => setMap(ns, n => this._nodes[n])), false, componentSuccessors);
return new Graph(
components.map(ns => setMap(ns, n => this._nodes[n])),
false,
componentSuccessors
);
}
makeDot(includeNode: (n: T) => boolean, nodeLabel: (n: T) => string): string {

View File

@ -166,7 +166,11 @@ function replaceUnion(
transformerForString = new DecodingTransformer(
graph,
getStringType(),
new ChoiceTransformer(graph, getStringType(), stringTypes.map(t => defined(transformerForStringType(t))))
new ChoiceTransformer(
graph,
getStringType(),
stringTypes.map(t => defined(transformerForStringType(t)))
)
);
}

View File

@ -79,7 +79,10 @@ export function sourcelikeToSource(sl: Sourcelike): Source {
}
return {
kind: "sequence",
sequence: arrayIntercalate(newline(), lines.map((l: string) => ({ kind: "text", text: l } as Source)))
sequence: arrayIntercalate(
newline(),
lines.map((l: string) => ({ kind: "text", text: l } as Source))
)
};
}
if (sl instanceof Name) {

View File

@ -974,9 +974,7 @@ export class MinMaxValueTransformer extends ProducerTransformer {
equals(other: any): boolean {
if (!super.equals(other)) return false;
return (
other instanceof MinMaxValueTransformer &&
this.minimum === other.minimum &&
this.maximum === other.maximum
other instanceof MinMaxValueTransformer && this.minimum === other.minimum && this.maximum === other.maximum
);
}
}

View File

@ -59,9 +59,11 @@ const transformedStringTypeTargetTypeKinds = {
"bool-string": { jsonSchema: "boolean", primitive: "bool" } as TransformedStringTypeTargets
};
export const transformedStringTypeTargetTypeKindsMap = mapFromObject(transformedStringTypeTargetTypeKinds as {
[kind: string]: TransformedStringTypeTargets;
});
export const transformedStringTypeTargetTypeKindsMap = mapFromObject(
transformedStringTypeTargetTypeKinds as {
[kind: string]: TransformedStringTypeTargets;
}
);
export type TransformedStringTypeKind = keyof typeof transformedStringTypeTargetTypeKinds;
export type PrimitiveStringTypeKind = "string" | TransformedStringTypeKind;
@ -642,7 +644,14 @@ export class MapType extends ObjectType {
readonly kind: "map";
constructor(typeRef: TypeRef, graph: TypeGraph, valuesRef: TypeRef | undefined) {
super(typeRef, graph, "map", false, definedMap(valuesRef, () => new Map()), valuesRef);
super(
typeRef,
graph,
"map",
false,
definedMap(valuesRef, () => new Map()),
valuesRef
);
}
// FIXME: Remove and use `getAdditionalProperties()` instead.

View File

@ -39,7 +39,12 @@ import {
TypeKind
} from "./Type";
import { TypeGraph, TypeRef, makeTypeRef, derefTypeRef, typeRefIndex, assertTypeRefGraph } from "./TypeGraph";
import { TypeAttributes, combineTypeAttributes, TypeAttributeKind, emptyTypeAttributes } from "./attributes/TypeAttributes";
import {
TypeAttributes,
combineTypeAttributes,
TypeAttributeKind,
emptyTypeAttributes
} from "./attributes/TypeAttributes";
import { defined, assert, panic } from "./support/Support";
import { stringTypesTypeAttributeKind, StringTypes } from "./attributes/StringTypes";
@ -276,7 +281,10 @@ export class TypeBuilder {
// we found the type based on its identity, i.e. all the identity
// attributes must be in there already, and we have a check that
// asserts that no identity attributes are added later.
this.addAttributes(result, mapFilter(attributes, (_, k) => !k.inIdentity));
this.addAttributes(
result,
mapFilter(attributes, (_, k) => !k.inIdentity)
);
}
return result;
}

View File

@ -1,7 +1,12 @@
import { setFilter, setSortBy, iterableFirst, setUnion, EqualityMap } from "collection-utils";
import { defined, panic, assert, assertNever } from "./support/Support";
import { TypeAttributes, combineTypeAttributes, emptyTypeAttributes, CombinationKind } from "./attributes/TypeAttributes";
import {
TypeAttributes,
combineTypeAttributes,
emptyTypeAttributes,
CombinationKind
} from "./attributes/TypeAttributes";
import {
Type,
PrimitiveType,
@ -105,7 +110,10 @@ export function makeGroupsToFlatten<T extends SetOperationType>(
}
export function combineTypeAttributesOfTypes(combinationKind: CombinationKind, types: Iterable<Type>): TypeAttributes {
return combineTypeAttributes(combinationKind, Array.from(types).map(t => t.getAttributes()));
return combineTypeAttributes(
combinationKind,
Array.from(types).map(t => t.getAttributes())
);
}
export function isAnyOrNull(t: Type): boolean {
@ -174,11 +182,12 @@ export type SeparatedNamedTypes = {
};
export function separateNamedTypes(types: Iterable<Type>): SeparatedNamedTypes {
const objects = (setFilter(types, t => t.kind === "object" || t.kind === "class") as Set<
ObjectType
>) as ReadonlySet<ObjectType>;
const enums = (setFilter(types, t => t instanceof EnumType) as Set<EnumType>) as ReadonlySet<EnumType>;
const unions = (setFilter(types, t => t instanceof UnionType) as Set<UnionType>) as ReadonlySet<UnionType>;
const objects = setFilter(
types,
t => t.kind === "object" || t.kind === "class"
) as Set<ObjectType> as ReadonlySet<ObjectType>;
const enums = setFilter(types, t => t instanceof EnumType) as Set<EnumType> as ReadonlySet<EnumType>;
const unions = setFilter(types, t => t instanceof UnionType) as Set<UnionType> as ReadonlySet<UnionType>;
return { objects, enums, unions };
}

View File

@ -63,9 +63,11 @@ function getCliqueProperties(
return [unifiedProperties, unifiedAdditionalProperties, lostTypeAttributes];
}
function countProperties(
clique: ObjectType[]
): { hasProperties: boolean; hasAdditionalProperties: boolean; hasNonAnyAdditionalProperties: boolean } {
function countProperties(clique: ObjectType[]): {
hasProperties: boolean;
hasAdditionalProperties: boolean;
hasNonAnyAdditionalProperties: boolean;
} {
let hasProperties = false;
let hasAdditionalProperties = false;
let hasNonAnyAdditionalProperties = false;
@ -119,7 +121,10 @@ export class UnifyUnionBuilder extends UnionBuilder<BaseGraphRewriteBuilder, Typ
if (!this._makeObjectTypes && (hasNonAnyAdditionalProperties || (!hasProperties && hasAdditionalProperties))) {
const propertyTypes = new Set<TypeRef>();
for (const o of objectTypes) {
setUnionInto(propertyTypes, Array.from(o.getProperties().values()).map(cp => cp.typeRef));
setUnionInto(
propertyTypes,
Array.from(o.getProperties().values()).map(cp => cp.typeRef)
);
}
const additionalPropertyTypes = new Set(
objectTypes

View File

@ -261,7 +261,10 @@ function attributesForTypes(types: Iterable<Type>): [ReadonlyMap<Type, TypeAttri
const resultAttributes = mapMap(unionsForType, (unionForType, t) => {
const singleAncestors = Array.from(unionForType).filter(u => defined(typesForUnion.get(u)).size === 1);
assert(singleAncestors.every(u => defined(typesForUnion.get(u)).has(t)), "We messed up bookkeeping");
assert(
singleAncestors.every(u => defined(typesForUnion.get(u)).has(t)),
"We messed up bookkeeping"
);
const inheritedAttributes = singleAncestors.map(u => u.getAttributes());
return combineTypeAttributes("union", [t.getAttributes()].concat(inheritedAttributes));
});

View File

@ -92,9 +92,8 @@ class UnionIdentifierTypeAttributeKind extends TypeAttributeKind<ReadonlySet<num
}
}
export const unionIdentifierTypeAttributeKind: TypeAttributeKind<
ReadonlySet<number>
> = new UnionIdentifierTypeAttributeKind();
export const unionIdentifierTypeAttributeKind: TypeAttributeKind<ReadonlySet<number>> =
new UnionIdentifierTypeAttributeKind();
let nextUnionIdentifier: number = 0;
@ -118,9 +117,8 @@ class UnionMemberNamesTypeAttributeKind extends TypeAttributeKind<Map<number, Ac
}
}
export const unionMemberNamesTypeAttributeKind: TypeAttributeKind<
Map<number, AccessorEntry>
> = new UnionMemberNamesTypeAttributeKind();
export const unionMemberNamesTypeAttributeKind: TypeAttributeKind<Map<number, AccessorEntry>> =
new UnionMemberNamesTypeAttributeKind();
export function makeUnionMemberNamesAttribute(unionAttributes: TypeAttributes, entry: AccessorEntry): TypeAttributes {
const identifiers = defined(unionIdentifierTypeAttributeKind.tryGetInAttributes(unionAttributes));

View File

@ -77,9 +77,8 @@ class PropertyDescriptionsTypeAttributeKind extends TypeAttributeKind<Map<string
}
}
export const propertyDescriptionsTypeAttributeKind: TypeAttributeKind<
Map<string, ReadonlySet<string>>
> = new PropertyDescriptionsTypeAttributeKind();
export const propertyDescriptionsTypeAttributeKind: TypeAttributeKind<Map<string, ReadonlySet<string>>> =
new PropertyDescriptionsTypeAttributeKind();
function isPropertiesKey(el: PathElement): boolean {
return el.kind === PathElementKind.KeyOrIndex && el.key === "properties";

View File

@ -1,6 +1,4 @@
import {
mapMap,
} from "collection-utils";
import { mapMap } from "collection-utils";
import { lookupKey, AccessorNames, makeAccessorNames } from "./AccessorNames";
import { EnumType } from "../Type";
@ -30,7 +28,6 @@ export function enumValuesAttributeProducer(
_canonicalRef: Ref | undefined,
_types: Set<JSONSchemaType>
): JSONSchemaAttributes | undefined {
if (typeof schema !== "object") return undefined;
const maybeEnumValues = schema["qt-enum-values"];

View File

@ -22,7 +22,15 @@ export { RenderContext } from "./Renderer";
export { Option, OptionDefinition, getOptionValues, OptionValues } from "./RendererOptions";
export { TargetLanguage, MultiFileRenderResult } from "./TargetLanguage";
export { all as defaultTargetLanguages, languageNamed } from "./language/All";
export { MultiWord, Sourcelike, SerializedRenderResult, Annotation, modifySource, singleWord, parenIfNeeded } from "./Source";
export {
MultiWord,
Sourcelike,
SerializedRenderResult,
Annotation,
modifySource,
singleWord,
parenIfNeeded
} from "./Source";
export { Name, funPrefixNamer, Namer } from "./Naming";
export { IssueAnnotationData } from "./Annotation";
export {
@ -75,16 +83,16 @@ export { ConvenienceRenderer } from "./ConvenienceRenderer";
export { uriTypeAttributeKind } from "./attributes/URIAttributes";
export { CPlusPlusTargetLanguage, CPlusPlusRenderer, cPlusPlusOptions } from "./language/CPlusPlus";
export {
CSharpTargetLanguage,
cSharpOptions,
CSharpRenderer
} from "./language/CSharp";
export { CSharpTargetLanguage, cSharpOptions, CSharpRenderer } from "./language/CSharp";
export { GoTargetLanguage, GoRenderer, goOptions } from "./language/Golang";
export { ObjectiveCTargetLanguage, ObjectiveCRenderer, objcOptions } from "./language/Objective-C";
export { JavaTargetLanguage, JavaRenderer, javaOptions } from "./language/Java";
export { JavaScriptTargetLanguage, JavaScriptRenderer, javaScriptOptions } from "./language/JavaScript";
export { JavaScriptPropTypesTargetLanguage, JavaScriptPropTypesRenderer, javaScriptPropTypesOptions } from "./language/JavaScriptPropTypes";
export {
JavaScriptPropTypesTargetLanguage,
JavaScriptPropTypesRenderer,
javaScriptPropTypesOptions
} from "./language/JavaScriptPropTypes";
export {
TypeScriptTargetLanguage,
TypeScriptRenderer,

View File

@ -1,6 +1,6 @@
import {JSONSchema, JSONSchemaStore} from "./JSONSchemaStore";
import {parseJSON} from "..";
import {readFromFileOrURL} from "./io/NodeIO";
import { JSONSchema, JSONSchemaStore } from "./JSONSchemaStore";
import { parseJSON } from "..";
import { readFromFileOrURL } from "./io/NodeIO";
export class FetchingJSONSchemaStore extends JSONSchemaStore {
constructor(private readonly _httpHeaders?: string[]) {

View File

@ -119,14 +119,7 @@ function normalizeURI(uri: string | URI): URI {
if (typeof uri === "string") {
uri = new URI(uri);
}
return new URI(
URI.decode(
uri
.clone()
.normalize()
.toString()
)
);
return new URI(URI.decode(uri.clone().normalize().toString()));
}
export class Ref {
@ -1210,12 +1203,7 @@ export class JSONSchemaInput implements Input<JSONSchemaSourceData> {
} else {
normalizedURIs = uris.map(uri => {
const normalizedURI = normalizeURI(uri);
if (
normalizedURI
.clone()
.hash("")
.toString() === ""
) {
if (normalizedURI.clone().hash("").toString() === "") {
normalizedURI.path(name);
}
return normalizedURI;

View File

@ -1,9 +1,9 @@
import * as fs from "fs";
import {Readable} from "readable-stream";
import {isNode} from "browser-or-node";
import {getStream} from "./get-stream";
import { Readable } from "readable-stream";
import { isNode } from "browser-or-node";
import { getStream } from "./get-stream";
import {messageError, panic} from "../../index";
import { messageError, panic } from "../../index";
// The typings for this module are screwy
const isURL = require("is-url");
@ -18,24 +18,21 @@ function parseHeaders(httpHeaders?: string[]): HttpHeaders {
return {};
}
return httpHeaders.reduce(
function(result: HttpHeaders, httpHeader: string) {
if (httpHeader !== undefined && httpHeader.length > 0) {
const split = httpHeader.indexOf(":");
return httpHeaders.reduce(function (result: HttpHeaders, httpHeader: string) {
if (httpHeader !== undefined && httpHeader.length > 0) {
const split = httpHeader.indexOf(":");
if (split < 0) {
return panic(`Could not parse HTTP header "${httpHeader}".`);
}
const key = httpHeader.slice(0, split).trim();
const value = httpHeader.slice(split + 1).trim();
result[key] = value;
if (split < 0) {
return panic(`Could not parse HTTP header "${httpHeader}".`);
}
return result;
},
{} as HttpHeaders
);
const key = httpHeader.slice(0, split).trim();
const value = httpHeader.slice(split + 1).trim();
result[key] = value;
}
return result;
}, {} as HttpHeaders);
}
export async function readableFromFileOrURL(fileOrURL: string, httpHeaders?: string[]): Promise<Readable> {
@ -47,7 +44,7 @@ export async function readableFromFileOrURL(fileOrURL: string, httpHeaders?: str
return response.body;
} else if (isNode) {
if (fileOrURL === "-") {
// Cast node readable to isomorphic readable from readable-stream
// Cast node readable to isomorphic readable from readable-stream
return process.stdin as unknown as Readable;
}
const filePath = fs.lstatSync(fileOrURL).isSymbolicLink() ? fs.readlinkSync(fileOrURL) : fileOrURL;

View File

@ -42,7 +42,7 @@ export const all: TargetLanguage[] = [
new DartTargetLanguage(),
new PythonTargetLanguage("Python", ["python", "py"], "py"),
new PikeTargetLanguage(),
new HaskellTargetLanguage(),
new HaskellTargetLanguage()
];
export function languageNamed(name: string, targetLanguages?: TargetLanguage[]): TargetLanguage | undefined {

View File

@ -43,40 +43,58 @@ export const cPlusPlusOptions = {
typeSourceStyle: new EnumOption(
"source-style",
"Source code generation type, whether to generate single or multiple source files",
[["single-source", true], ["multi-source", false]],
[
["single-source", true],
["multi-source", false]
],
"single-source",
"secondary"
),
includeLocation: new EnumOption(
"include-location",
"Whether json.hpp is to be located globally or locally",
[["local-include", true], ["global-include", false]],
[
["local-include", true],
["global-include", false]
],
"local-include",
"secondary"
),
codeFormat: new EnumOption(
"code-format",
"Generate classes with getters/setters, instead of structs",
[["with-struct", false], ["with-getter-setter", true]],
[
["with-struct", false],
["with-getter-setter", true]
],
"with-getter-setter"
),
wstring: new EnumOption(
"wstring",
"Store strings using Utf-16 std::wstring, rather than Utf-8 std::string",
[["use-string", false], ["use-wstring", true]],
[
["use-string", false],
["use-wstring", true]
],
"use-string"
),
msbuildPermissive: new EnumOption(
"msbuildPermissive",
"Moves to_json and from_json types into the nlohmann::details namespace, so that msbuild can build it with conformance mode disabled",
[["not-permissive", false], ["use-permissive", true]],
[
["not-permissive", false],
["use-permissive", true]
],
"not-permissive",
"secondary"
),
westConst: new EnumOption(
"const-style",
"Put const to the left/west (const T) or right/east (T const)",
[["west-const", true], ["east-const", false]],
[
["west-const", true],
["east-const", false]
],
"west-const"
),
justTypes: new BooleanOption("just-types", "Plain types only", false),
@ -107,7 +125,7 @@ export const cPlusPlusOptions = {
camelUpperAcronymsValue
]),
boost: new BooleanOption("boost", "Require a dependency on boost. Without boost, C++17 is required", true),
hideNullOptional: new BooleanOption("hide-null-optional", "Hide null value for optional field", false),
hideNullOptional: new BooleanOption("hide-null-optional", "Hide null value for optional field", false)
};
export class CPlusPlusTargetLanguage extends TargetLanguage {
@ -130,7 +148,7 @@ export class CPlusPlusTargetLanguage extends TargetLanguage {
cPlusPlusOptions.enumeratorNamingStyle,
cPlusPlusOptions.enumType,
cPlusPlusOptions.boost,
cPlusPlusOptions.hideNullOptional,
cPlusPlusOptions.hideNullOptional
];
}
@ -655,9 +673,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
if (this.leadingComments !== undefined) {
this.emitCommentLines(this.leadingComments);
} else if (!this._options.justTypes) {
this.emitCommentLines([
" To parse this JSON data, first install",
"",]);
this.emitCommentLines([" To parse this JSON data, first install", ""]);
if (this._options.boost) {
this.emitCommentLines([" Boost http://www.boost.org"]);
}
@ -823,11 +839,13 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
}
protected ourQualifier(inJsonNamespace: boolean): Sourcelike {
return (inJsonNamespace || this._options.msbuildPermissive) ? [arrayIntercalate("::", this._namespaceNames), "::"] : [];
return inJsonNamespace || this._options.msbuildPermissive
? [arrayIntercalate("::", this._namespaceNames), "::"]
: [];
}
protected jsonQualifier(inJsonNamespace: boolean): Sourcelike {
return (inJsonNamespace || this._options.msbuildPermissive) ? [] : "nlohmann::";
return inJsonNamespace || this._options.msbuildPermissive ? [] : "nlohmann::";
}
protected variantIndirection(needIndirection: boolean, typeSrc: Sourcelike): Sourcelike {
@ -835,9 +853,16 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
return [optionalType, "<", typeSrc, ">"];
}
protected cppType(t: Type, ctx: TypeContext, withIssues: boolean, forceNarrowString: boolean, isOptional: boolean): Sourcelike {
protected cppType(
t: Type,
ctx: TypeContext,
withIssues: boolean,
forceNarrowString: boolean,
isOptional: boolean
): Sourcelike {
const inJsonNamespace = ctx.inJsonNamespace;
if (isOptional && t instanceof UnionType) { // avoid have optionalType<optionalType<Type>>
if (isOptional && t instanceof UnionType) {
// avoid have optionalType<optionalType<Type>>
for (const tChild of t.getChildren()) {
if (tChild.isNullable) {
isOptional = false;
@ -849,11 +874,17 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
t,
_anyType => {
isOptional = false;
return maybeAnnotated(withIssues, anyTypeIssueAnnotation, [this.jsonQualifier(inJsonNamespace), "json"]);
return maybeAnnotated(withIssues, anyTypeIssueAnnotation, [
this.jsonQualifier(inJsonNamespace),
"json"
]);
},
_nullType => {
isOptional = false;
return maybeAnnotated(withIssues, nullTypeIssueAnnotation, [this.jsonQualifier(inJsonNamespace), "json"]);
return maybeAnnotated(withIssues, nullTypeIssueAnnotation, [
this.jsonQualifier(inJsonNamespace),
"json"
]);
},
_boolType => "bool",
_integerType => "int64_t",
@ -877,10 +908,10 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
">"
],
classType =>
this.variantIndirection(ctx.needsForwardIndirection && this.isForwardDeclaredType(classType) && !isOptional, [
this.ourQualifier(inJsonNamespace),
this.nameForNamedType(classType)
]),
this.variantIndirection(
ctx.needsForwardIndirection && this.isForwardDeclaredType(classType) && !isOptional,
[this.ourQualifier(inJsonNamespace), this.nameForNamedType(classType)]
),
mapType => {
let keyType = this._stringType.getType();
if (forceNarrowString) {
@ -914,14 +945,8 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
);
}
);
if (!isOptional)
return typeSource;
return [
optionalType,
"<",
typeSource,
">"
];
if (!isOptional) return typeSource;
return [optionalType, "<", typeSource, ">"];
}
/**
@ -1055,7 +1080,10 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
* a member called 'value' value = value will screw up the compiler
*/
const checkConst = this.lookupGlobalName(GlobalNames.CheckConstraint);
if ((property.type instanceof UnionType && property.type.findMember("null") !== undefined) || (property.isOptional && property.type.kind !== "null" && property.type.kind !== "any")) {
if (
(property.type instanceof UnionType && property.type.findMember("null") !== undefined) ||
(property.isOptional && property.type.kind !== "null" && property.type.kind !== "any")
) {
this.emitLine(rendered, " ", getterName, "() const { return ", name, "; }");
if (constraints !== undefined && constraints.has(jsonName)) {
this.emitLine(
@ -1122,15 +1150,26 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
res.set(jsonName, [
this.constraintMember(jsonName),
"(",
(minMax !== undefined && minMax[0] !== undefined) ? String(minMax[0]) : this._nulloptType,
minMax !== undefined && minMax[0] !== undefined ? String(minMax[0]) : this._nulloptType,
", ",
(minMax !== undefined && minMax[1] !== undefined) ? String(minMax[1]) : this._nulloptType,
minMax !== undefined && minMax[1] !== undefined ? String(minMax[1]) : this._nulloptType,
", ",
(minMaxLength !== undefined && minMaxLength[0] !== undefined) ? String(minMaxLength[0]) : this._nulloptType,
minMaxLength !== undefined && minMaxLength[0] !== undefined
? String(minMaxLength[0])
: this._nulloptType,
", ",
(minMaxLength !== undefined && minMaxLength[1] !== undefined) ? String(minMaxLength[1]) : this._nulloptType,
minMaxLength !== undefined && minMaxLength[1] !== undefined
? String(minMaxLength[1])
: this._nulloptType,
", ",
(pattern === undefined) ? this._nulloptType : [this._stringType.getType(), "(", this._stringType.createStringLiteral([stringEscape(pattern)]), ")"],
pattern === undefined
? this._nulloptType
: [
this._stringType.getType(),
"(",
this._stringType.createStringLiteral([stringEscape(pattern)]),
")"
],
")"
]);
});
@ -1196,7 +1235,8 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.emitBlock(
["inline void from_json(", this.withConst("json"), " & j, ", ourQualifier, className, "& x)"],
false, () => {
false,
() => {
cppType = this.cppType(
t,
{ needsForwardIndirection: true, needsOptionalIndirection: true, inJsonNamespace: true },
@ -1214,14 +1254,20 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.emitLine([
"x = ",
this._stringType.wrapEncodingChange([ourQualifier], cppType, toType, ["j.get<", cppType, ">()"]),
this._stringType.wrapEncodingChange([ourQualifier], cppType, toType, [
"j.get<",
cppType,
">()"
]),
";"
]);
});
}
);
this.emitBlock(
["inline void to_json(json & j, ", this.withConst([ourQualifier, className]), " & x)"],
false, () => {
false,
() => {
cppType = this.cppType(
t,
{ needsForwardIndirection: true, needsOptionalIndirection: true, inJsonNamespace: true },
@ -1242,7 +1288,8 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this._stringType.wrapEncodingChange([ourQualifier], cppType, toType, "x"),
";"
]);
});
}
);
}
}
@ -1253,7 +1300,8 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.emitBlock(
["inline void from_json(", this.withConst("json"), " & j, ", ourQualifier, className, "& x)"],
false, () => {
false,
() => {
this.forEachClassProperty(c, "none", (name, json, p) => {
const [, , setterName] = defined(this._gettersAndSettersForPropertyName.get(name));
const t = p.type;
@ -1286,7 +1334,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
return;
}
if (p.isOptional || t instanceof UnionType) {
const [nullOrOptional, typeSet] = function (): [boolean, ReadonlySet<Type>] {
const [nullOrOptional, typeSet] = (function (): [boolean, ReadonlySet<Type>] {
if (t instanceof UnionType) {
const [maybeNull, nonNulls] = removeNullFromUnion(t, true);
return [maybeNull !== null || p.isOptional, nonNulls];
@ -1295,7 +1343,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
set.add(t);
return [true, set];
}
}();
})();
if (nullOrOptional) {
cppType = this.cppTypeInOptional(
typeSet,
@ -1379,12 +1427,14 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
";"
);
});
});
}
);
this.ensureBlankLine();
this.emitBlock(
["inline void to_json(json & j, ", this.withConst([ourQualifier, className]), " & x)"],
false, () => {
false,
() => {
this.emitLine("j = json::object();");
this.forEachClassProperty(c, "none", (name, json, p) => {
const t = p.type;
@ -1423,16 +1473,22 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
];
if (p.isOptional && this._options.hideNullOptional) {
this.emitBlock(
["if (", this._stringType.wrapEncodingChange([ourQualifier], cppType, toType, ["x.", getter]), ")"],
[
"if (",
this._stringType.wrapEncodingChange([ourQualifier], cppType, toType, ["x.", getter]),
")"
],
false,
() => {
this.emitLine(assignment);
});
}
);
} else {
this.emitLine(assignment);
}
});
});
}
);
}
protected emitEnum(e: EnumType, enumName: Name): void {
@ -1494,7 +1550,8 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.emitBlock(
["inline void from_json(", this.withConst("json"), " & j, ", variantType, " & x)"],
false, () => {
false,
() => {
let onFirst = true;
for (const [kind, func] of functionForKind) {
const typeForKind = iterableFind(nonNulls, t => t.kind === kind);
@ -1528,55 +1585,54 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
onFirst = false;
}
this.emitLine('else throw "Could not deserialize";');
});
}
);
this.ensureBlankLine();
this.emitBlock(
["inline void to_json(json & j, ", this.withConst(variantType), " & x)"],
false, () => {
this.emitBlock(["switch (x.", this._variantIndexMethodName, "())"], false, () => {
let i = 0;
for (const t of nonNulls) {
this.emitLine("case ", i.toString(), ":");
this.indent(() => {
const cppType = this.cppType(
t,
{
needsForwardIndirection: true,
needsOptionalIndirection: true,
inJsonNamespace: true
},
false,
false,
false
);
const toType = this.cppType(
t,
{
needsForwardIndirection: true,
needsOptionalIndirection: true,
inJsonNamespace: true
},
false,
true,
false
);
this.emitLine(
"j = ",
this._stringType.wrapEncodingChange([ourQualifier], cppType, toType, [
this._options.boost ? "boost::get<" : "std::get<",
cppType,
">(x)"
]),
";"
);
this.emitLine("break;");
});
i++;
}
this.emitLine('default: throw "Input JSON does not conform to schema";');
});
this.emitBlock(["inline void to_json(json & j, ", this.withConst(variantType), " & x)"], false, () => {
this.emitBlock(["switch (x.", this._variantIndexMethodName, "())"], false, () => {
let i = 0;
for (const t of nonNulls) {
this.emitLine("case ", i.toString(), ":");
this.indent(() => {
const cppType = this.cppType(
t,
{
needsForwardIndirection: true,
needsOptionalIndirection: true,
inJsonNamespace: true
},
false,
false,
false
);
const toType = this.cppType(
t,
{
needsForwardIndirection: true,
needsOptionalIndirection: true,
inJsonNamespace: true
},
false,
true,
false
);
this.emitLine(
"j = ",
this._stringType.wrapEncodingChange([ourQualifier], cppType, toType, [
this._options.boost ? "boost::get<" : "std::get<",
cppType,
">(x)"
]),
";"
);
this.emitLine("break;");
});
i++;
}
this.emitLine('default: throw "Input JSON does not conform to schema";');
});
});
}
protected emitEnumHeaders(enumName: Name): void {
@ -1597,27 +1653,39 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.emitBlock(
["inline void from_json(", this.withConst("json"), " & j, ", ourQualifier, enumName, " & x)"],
false, () => {
false,
() => {
if (this.isLargeEnum(e)) {
this.emitBlock(["static std::unordered_map<", this._stringType.getType(), ", ", ourQualifier, enumName, "> enumValues"], true, () => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine(
"{",
this._stringType.wrapEncodingChange(
[ourQualifier],
this._stringType.getType(),
this.NarrowString.getType(),
[this._stringType.createStringLiteral([stringEscape(jsonName)])]
),
", ",
ourQualifier,
enumName,
"::",
name,
"},"
);
});
});
this.emitBlock(
[
"static std::unordered_map<",
this._stringType.getType(),
", ",
ourQualifier,
enumName,
"> enumValues"
],
true,
() => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine(
"{",
this._stringType.wrapEncodingChange(
[ourQualifier],
this._stringType.getType(),
this.NarrowString.getType(),
[this._stringType.createStringLiteral([stringEscape(jsonName)])]
),
", ",
ourQualifier,
enumName,
"::",
name,
"},"
);
});
}
);
this.emitLine(`auto iter = enumValues.find(j.get<${this._stringType.getType()}>());`);
this.emitBlock("if (iter != enumValues.end())", false, () => {
@ -1647,12 +1715,14 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
});
this.emitLine('else throw "Input JSON does not conform to schema";');
}
});
}
);
this.ensureBlankLine();
this.emitBlock(
["inline void to_json(json & j, ", this.withConst([ourQualifier, enumName]), " & x)"],
false, () => {
false,
() => {
this.emitBlock("switch (x)", false, () => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine(
@ -1673,7 +1743,8 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
});
this.emitLine('default: throw "This should not happen";');
});
});
}
);
}
protected emitTopLevelTypedef(t: Type, name: Name): void {
@ -1731,18 +1802,25 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.emitNamespaces(["nlohmann"], () => {
this.emitLine("template <typename T>");
this.emitBlock(["struct adl_serializer<", optionalType, "<T>>"], true, () => {
this.emitBlock(["static void to_json(json & j, ", this.withConst([optionalType, "<T>"]), " & opt)"], false, () => {
this.emitLine("if (!opt) j = nullptr; else j = *opt;");
});
this.emitBlock(
["static void to_json(json & j, ", this.withConst([optionalType, "<T>"]), " & opt)"],
false,
() => {
this.emitLine("if (!opt) j = nullptr; else j = *opt;");
}
);
this.ensureBlankLine();
this.emitBlock(["static ", optionalType, "<T> from_json(", this.withConst("json"), " & j)"], false, () => {
this.emitLine(
`if (j.is_null()) return std::unique_ptr<T>(); else return std::unique_ptr<T>(new T(j.get<T>()));`
);
});
this.emitBlock(
["static ", optionalType, "<T> from_json(", this.withConst("json"), " & j)"],
false,
() => {
this.emitLine(
`if (j.is_null()) return std::unique_ptr<T>(); else return std::unique_ptr<T>(new T(j.get<T>()));`
);
}
);
});
});
@ -1862,7 +1940,15 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
const checkConst = this.lookupGlobalName(GlobalNames.CheckConstraint);
this.emitBlock(
["inline void ", checkConst, "(", this._stringType.getConstType(), " name, ", this.withConst(classConstraint), " & c, int64_t value)"],
[
"inline void ",
checkConst,
"(",
this._stringType.getConstType(),
" name, ",
this.withConst(classConstraint),
" & c, int64_t value)"
],
false,
() => {
this.emitBlock(
@ -1919,11 +2005,29 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.ensureBlankLine();
this.emitBlock(
["inline void ", checkConst, "(", this._stringType.getConstType(), " name, ", this.withConst(classConstraint), " & c, ", this._stringType.getConstType(), " value)"],
[
"inline void ",
checkConst,
"(",
this._stringType.getConstType(),
" name, ",
this.withConst(classConstraint),
" & c, ",
this._stringType.getConstType(),
" value)"
],
false,
() => {
this.emitBlock(
["if (c.", getterMinLength, "() != ", this._nulloptType, " && value.length() < *c.", getterMinLength, "())"],
[
"if (c.",
getterMinLength,
"() != ",
this._nulloptType,
" && value.length() < *c.",
getterMinLength,
"())"
],
false,
() => {
this.emitLine(
@ -1948,7 +2052,15 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.ensureBlankLine();
this.emitBlock(
["if (c.", getterMaxLength, "() != ", this._nulloptType, " && value.length() > *c.", getterMaxLength, "())"],
[
"if (c.",
getterMaxLength,
"() != ",
this._nulloptType,
" && value.length() > *c.",
getterMaxLength,
"())"
],
false,
() => {
this.emitLine(
@ -2035,23 +2147,25 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.emitLine("#define " + untypedMacroName);
this.emitBlock(
[
"inline json get_untyped(", this.withConst("json"), " & j, ", this.withConst("char"), " * property)"
], false, () => {
["inline json get_untyped(", this.withConst("json"), " & j, ", this.withConst("char"), " * property)"],
false,
() => {
this.emitBlock(["if (j.find(property) != j.end())"], false, () => {
this.emitLine("return j.at(property).get<json>();");
});
this.emitLine("return json();");
});
}
);
this.ensureBlankLine();
this.emitBlock(
[
"inline json get_untyped(", this.withConst("json"), " & j, std::string property)"
], false, () => {
["inline json get_untyped(", this.withConst("json"), " & j, std::string property)"],
false,
() => {
this.emitLine("return get_untyped(j, property.data());");
});
}
);
this.emitLine("#endif");
@ -2066,7 +2180,15 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.emitLine("template <typename T>");
this.emitBlock(
["inline ", optionalType, "<T> get_optional(", this.withConst("json"), " & j, ", this.withConst("char"), " * property)"],
[
"inline ",
optionalType,
"<T> get_optional(",
this.withConst("json"),
" & j, ",
this.withConst("char"),
" * property)"
],
false,
() => {
this.emitBlock(["if (j.find(property) != j.end())"], false, () => {
@ -2496,7 +2618,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
return originalType !== newType;
}
public NarrowString = new class extends BaseString implements StringType {
public NarrowString = new (class extends BaseString implements StringType {
constructor() {
super(
"std::string",
@ -2522,9 +2644,9 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
public emitHelperFunctions(): void {
return;
}
}();
})();
public WideString = new class extends BaseString implements StringType {
public WideString = new (class extends BaseString implements StringType {
constructor(public superThis: CPlusPlusRenderer) {
super(
"std::wstring",
@ -2652,16 +2774,16 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
this.superThis.ensureBlankLine();
this.superThis.emitLine("template<typename T>");
this.superThis.emitBlock(
["std::wstring wdump(const T& j)"],
false,
() => {
this.superThis.emitLine("std::ostringstream s;");
this.superThis.emitLine("s << j;");
this.superThis.emitLine("return ", this.superThis.ourQualifier(false), "Utf16_Utf8<std::string, std::wstring>::convert(s.str()); ");
}
);
this.superThis.emitBlock(["std::wstring wdump(const T& j)"], false, () => {
this.superThis.emitLine("std::ostringstream s;");
this.superThis.emitLine("s << j;");
this.superThis.emitLine(
"return ",
this.superThis.ourQualifier(false),
"Utf16_Utf8<std::string, std::wstring>::convert(s.str()); "
);
});
this.superThis.ensureBlankLine();
}
}(this);
})(this);
}

View File

@ -9,7 +9,7 @@ import {
ArrayType,
TransformedStringTypeKind,
PrimitiveStringTypeKind,
PrimitiveType,
PrimitiveType
} from "../Type";
import { matchType, nullableFromUnion, removeNullFromUnion, directlyReachableSingleNamedType } from "../TypeUtils";
import { Sourcelike, maybeAnnotated, modifySource } from "../Source";
@ -19,7 +19,7 @@ import {
splitIntoWords,
combineWords,
firstUpperWordStyle,
camelCase,
camelCase
} from "../support/Strings";
import { defined, assert, panic, assertNever } from "../support/Support";
import { Name, DependencyName, Namer, funPrefixNamer, SimpleName } from "../Naming";
@ -46,7 +46,7 @@ import {
ArrayDecodingTransformer,
ArrayEncodingTransformer,
MinMaxLengthCheckTransformer,
MinMaxValueTransformer,
MinMaxValueTransformer
} from "../Transformers";
import { RenderContext } from "../Renderer";
import { minMaxLengthForType, minMaxValueForType } from "../attributes/Constraints";
@ -64,7 +64,7 @@ export type OutputFeatures = { helpers: boolean; attributes: boolean };
export enum AccessModifier {
None,
Public,
Internal,
Internal
}
export type CSharpTypeForAny = "object" | "dynamic";
@ -121,19 +121,22 @@ export const cSharpOptions = {
framework: new EnumOption(
"framework",
"Serialization framework",
[["NewtonSoft", Framework.Newtonsoft], ["SystemTextJson", Framework.SystemTextJson]],
[
["NewtonSoft", Framework.Newtonsoft],
["SystemTextJson", Framework.SystemTextJson]
],
"NewtonSoft"
),
useList: new EnumOption("array-type", "Use T[] or List<T>", [
["array", false],
["list", true],
["list", true]
]),
dense: new EnumOption(
"density",
"Property density",
[
["normal", false],
["dense", true],
["dense", true]
],
"normal",
"secondary"
@ -145,22 +148,18 @@ export const cSharpOptions = {
"C# version",
[
["5", 5],
["6", 6],
["6", 6]
],
"6",
"secondary"
),
virtual: new BooleanOption(
"virtual",
"Generate virtual properties",
false
),
virtual: new BooleanOption("virtual", "Generate virtual properties", false),
typeForAny: new EnumOption<CSharpTypeForAny>(
"any-type",
'Type to use for "any"',
[
["object", "object"],
["dynamic", "dynamic"],
["dynamic", "dynamic"]
],
"object",
"secondary"
@ -170,11 +169,11 @@ export const cSharpOptions = {
"Type to use for numbers",
[
["double", false],
["decimal", true],
["decimal", true]
],
"double",
"secondary"
),
)
};
export class CSharpTargetLanguage extends TargetLanguage {
@ -191,7 +190,7 @@ export class CSharpTargetLanguage extends TargetLanguage {
cSharpOptions.useList,
cSharpOptions.useDecimal,
cSharpOptions.typeForAny,
cSharpOptions.virtual,
cSharpOptions.virtual
];
}
@ -228,9 +227,17 @@ export class CSharpTargetLanguage extends TargetLanguage {
switch (options.framework) {
case Framework.Newtonsoft:
return new NewtonsoftCSharpRenderer(this, renderContext, getOptionValues(newtonsoftCSharpOptions, untypedOptionValues));
return new NewtonsoftCSharpRenderer(
this,
renderContext,
getOptionValues(newtonsoftCSharpOptions, untypedOptionValues)
);
case Framework.SystemTextJson:
return new SystemTextJsonCSharpRenderer(this, renderContext, getOptionValues(systemTextJsonCSharpOptions, untypedOptionValues));
return new SystemTextJsonCSharpRenderer(
this,
renderContext,
getOptionValues(systemTextJsonCSharpOptions, untypedOptionValues)
);
default:
return assertNever(options.framework);
}
@ -305,9 +312,9 @@ export class CSharpRenderer extends ConvenienceRenderer {
"Equals",
"GetType",
"MemberwiseClone",
"ReferenceEquals",
"ReferenceEquals"
],
includeGlobalForbidden: false,
includeGlobalForbidden: false
};
}
@ -356,13 +363,13 @@ export class CSharpRenderer extends ConvenienceRenderer {
const actualType = follow(t);
return matchType<Sourcelike>(
actualType,
(_anyType) => maybeAnnotated(withIssues, anyTypeIssueAnnotation, this._csOptions.typeForAny),
(_nullType) => maybeAnnotated(withIssues, nullTypeIssueAnnotation, this._csOptions.typeForAny),
(_boolType) => "bool",
(_integerType) => "long",
(_doubleType) => this.doubleType,
(_stringType) => "string",
(arrayType) => {
_anyType => maybeAnnotated(withIssues, anyTypeIssueAnnotation, this._csOptions.typeForAny),
_nullType => maybeAnnotated(withIssues, nullTypeIssueAnnotation, this._csOptions.typeForAny),
_boolType => "bool",
_integerType => "long",
_doubleType => this.doubleType,
_stringType => "string",
arrayType => {
const itemsType = this.csType(arrayType.items, follow, withIssues);
if (this._csOptions.useList) {
return ["System.Collections.Generic.List<", itemsType, ">"];
@ -370,15 +377,19 @@ export class CSharpRenderer extends ConvenienceRenderer {
return [itemsType, "[]"];
}
},
(classType) => this.nameForNamedType(classType),
(mapType) => ["System.Collections.Generic.Dictionary<string, ", this.csType(mapType.values, follow, withIssues), ">"],
(enumType) => this.nameForNamedType(enumType),
(unionType) => {
classType => this.nameForNamedType(classType),
mapType => [
"System.Collections.Generic.Dictionary<string, ",
this.csType(mapType.values, follow, withIssues),
">"
],
enumType => this.nameForNamedType(enumType),
unionType => {
const nullable = nullableFromUnion(unionType);
if (nullable !== null) return this.nullableCSType(nullable, noFollow);
return this.nameForNamedType(unionType);
},
(transformedStringType) => csTypeForTransformedStringType(transformedStringType)
transformedStringType => csTypeForTransformedStringType(transformedStringType)
);
}
@ -442,8 +453,7 @@ export class CSharpRenderer extends ConvenienceRenderer {
const propertyArray = ["public "];
if (this._csOptions.virtual)
propertyArray.push("virtual ");
if (this._csOptions.virtual) propertyArray.push("virtual ");
return [...propertyArray, csType, " ", name, " { get; set; }"];
}
@ -524,9 +534,9 @@ export class CSharpRenderer extends ConvenienceRenderer {
this.emitLine("public ", csType, " ", fieldName, ";");
});
this.ensureBlankLine();
const nullTests: Sourcelike[] = Array.from(nonNulls).map((t) => [
const nullTests: Sourcelike[] = Array.from(nonNulls).map(t => [
this.nameForUnionMember(u, t),
" == null",
" == null"
]);
this.ensureBlankLine();
this.forEachUnionMember(u, nonNulls, "none", null, (fieldName, t) => {
@ -544,7 +554,7 @@ export class CSharpRenderer extends ConvenienceRenderer {
private emitEnumDefinition(e: EnumType, enumName: Name): void {
const caseNames: Sourcelike[] = [];
this.forEachEnumCase(e, "none", (name) => {
this.forEachEnumCase(e, "none", name => {
if (caseNames.length > 0) caseNames.push(", ");
caseNames.push(name);
});
@ -653,19 +663,19 @@ export const newtonsoftCSharpOptions = Object.assign({}, cSharpOptions, {
["complete", { namespaces: true, helpers: true, attributes: true }],
["attributes-only", { namespaces: true, helpers: false, attributes: true }],
["just-types-and-namespace", { namespaces: true, helpers: false, attributes: false }],
["just-types", { namespaces: true, helpers: false, attributes: false }],
["just-types", { namespaces: true, helpers: false, attributes: false }]
]),
baseclass: new EnumOption(
"base-class",
"Base class",
[
["EntityData", "EntityData"],
["Object", undefined],
["Object", undefined]
],
"Object",
"secondary"
),
checkRequired: new BooleanOption("check-required", "Fail if required properties are missing", false),
checkRequired: new BooleanOption("check-required", "Fail if required properties are missing", false)
});
export class NewtonsoftCSharpRenderer extends CSharpRenderer {
@ -698,7 +708,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
"MetadataPropertyHandling",
"DateParseHandling",
"FromJson",
"Required",
"Required"
];
if (this._options.dense) {
forbidden.push("J", "R", "N");
@ -723,7 +733,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
}
return new SimpleName([`${xfer.kind}_converter`], namingFunction, inferredNameOrder + 30);
}
return new DependencyName(namingFunction, typeName.order + 30, (lookup) => `${lookup(typeName)}_converter`);
return new DependencyName(namingFunction, typeName.order + 30, lookup => `${lookup(typeName)}_converter`);
}
protected makeNamedTypeDependencyNames(t: Type, name: Name): DependencyName[] {
@ -732,7 +742,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
const extensionsName = new DependencyName(
namingFunction,
name.order + 30,
(lookup) => `${lookup(name)}_extensions`
lookup => `${lookup(name)}_extensions`
);
this._enumExtensionsNames.set(name, extensionsName);
return [extensionsName];
@ -900,7 +910,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
// Sometimes multiple top-levels will resolve to the same type, so we have to take care
// not to emit more than one extension method for the same type.
const seenTypes = new Set<Type>();
this.forEachTopLevel("none", (t) => {
this.forEachTopLevel("none", t => {
// FIXME: Make ToJson a Named
if (!seenTypes.has(t)) {
seenTypes.add(t);
@ -1034,7 +1044,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
this.emitDecodeTransformer(
xfer.itemTransformer,
xfer.itemTargetType,
(v) => this.emitLine(variableName, ".Add(", v, ");"),
v => this.emitLine(variableName, ".Add(", v, ");"),
"arrayItem"
);
// FIXME: handle EOF
@ -1125,7 +1135,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
if (xfer instanceof ChoiceTransformer) {
const caseXfers = xfer.transformers;
if (caseXfers.length > 1 && caseXfers.every((caseXfer) => caseXfer instanceof StringMatchTransformer)) {
if (caseXfers.length > 1 && caseXfers.every(caseXfer => caseXfer instanceof StringMatchTransformer)) {
this.emitLine("switch (", variable, ")");
this.emitBlock(() => {
for (const caseXfer of caseXfers) {
@ -1347,9 +1357,7 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
this.emitLine("if (reader.TokenType == JsonToken.Null) return null;");
}
const allHandled = this.emitDecodeTransformer(xfer, targetType, (v) =>
this.emitLine("return ", v, ";")
);
const allHandled = this.emitDecodeTransformer(xfer, targetType, v => this.emitLine("return ", v, ";"));
if (!allHandled) {
this.emitThrow(['"Cannot unmarshal type ', csType, '"']);
}
@ -1400,19 +1408,19 @@ export const systemTextJsonCSharpOptions = Object.assign({}, cSharpOptions, {
["complete", { namespaces: true, helpers: true, attributes: true }],
["attributes-only", { namespaces: true, helpers: false, attributes: true }],
["just-types-and-namespace", { namespaces: true, helpers: false, attributes: false }],
["just-types", { namespaces: true, helpers: false, attributes: false }],
["just-types", { namespaces: true, helpers: false, attributes: false }]
]),
baseclass: new EnumOption(
"base-class",
"Base class",
[
["EntityData", "EntityData"],
["Object", undefined],
["Object", undefined]
],
"Object",
"secondary"
),
checkRequired: new BooleanOption("check-required", "Fail if required properties are missing", false),
checkRequired: new BooleanOption("check-required", "Fail if required properties are missing", false)
});
export class SystemTextJsonCSharpRenderer extends CSharpRenderer {
@ -1673,10 +1681,8 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer {
}
private serializeValueCode(value: Sourcelike): Sourcelike {
if (value !== "null")
return ["JsonSerializer.Serialize(writer, ", value, ", options)"];
else
return ["writer.WriteNullValue()"];
if (value !== "null") return ["JsonSerializer.Serialize(writer, ", value, ", options)"];
else return ["writer.WriteNullValue()"];
}
private emitSerializeClass(): void {
@ -1704,14 +1710,18 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer {
private emitReadJson(emitBody: () => void, csType: Sourcelike): void {
this.emitLine(
"public override ", csType, " Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)"
"public override ",
csType,
" Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)"
);
this.emitBlock(emitBody);
}
private emitWriteJson(variable: string, emitBody: () => void, csType: Sourcelike): void {
this.emitLine(
"public override void Write(Utf8JsonWriter writer, ", csType, " ",
"public override void Write(Utf8JsonWriter writer, ",
csType,
" ",
variable,
", JsonSerializerOptions options)"
);
@ -1728,7 +1738,9 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer {
const converterName: Sourcelike = ["Converter"];
this.emitType(undefined, AccessModifier.Internal, "static class", converterName, undefined, () => {
// Do not use .Web as defaults. That turns on caseInsensitive property names and will fail the keywords test.
this.emitLine("public static readonly JsonSerializerOptions Settings = new(JsonSerializerDefaults.General)");
this.emitLine(
"public static readonly JsonSerializerOptions Settings = new(JsonSerializerDefaults.General)"
);
this.emitBlock(() => {
// this.emitLine("MetadataPropertyHandling = MetadataPropertyHandling.Ignore,");
// this.emitLine("DateParseHandling = DateParseHandling.None,");
@ -1862,7 +1874,13 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer {
targetType,
emitFinish
);
this.emitDecoderTransformerCase(["True", "False"], "boolValue", xfer.boolTransformer, targetType, emitFinish);
this.emitDecoderTransformerCase(
["True", "False"],
"boolValue",
xfer.boolTransformer,
targetType,
emitFinish
);
this.emitDecoderTransformerCase(
// ["String", "Date"],
["String"],
@ -2016,7 +2034,7 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer {
this.emitBlock(() => {
// this.emitLine("var uri = new Uri(", variable, ");");
// The default value about:blank should never happen, but this way we avoid a null reference warning.
this.emitLine("var uri = new Uri(\"about:blank\");");
this.emitLine('var uri = new Uri("about:blank");');
this.emitLine("if (!string.IsNullOrEmpty(stringValue))");
this.emitBlock(() => {
this.emitLine("uri = new Uri(", variable, ");");
@ -2131,46 +2149,60 @@ export class SystemTextJsonCSharpRenderer extends CSharpRenderer {
// converterName = ['Nullable', converterName];
// csType = [csType, "?"];
// }
this.emitType(undefined, AccessModifier.Internal, "class", converterName, ["JsonConverter<", csType, ">"], () => {
let canConvertExpr: Sourcelike = ["t == typeof(", csType, ")"];
this.emitCanConvert(canConvertExpr);
this.ensureBlankLine();
this.emitReadJson(() => {
// FIXME: It's unsatisfying that we need this. The reason is that we not
// only match T, but also T?. If we didn't, then the T in T? would not be
// deserialized with our converter but with the default one. Can we check
// whether the type is a nullable?
// FIXME: This could duplicate one of the cases handled below in
// `emitDecodeTransformer`.
// if (haveNullable && !(targetType instanceof UnionType)) {
// this.emitLine("if (reader.TokenType == JsonTokenType.Null) return null;");
// }
this.emitType(
undefined,
AccessModifier.Internal,
"class",
converterName,
["JsonConverter<", csType, ">"],
() => {
let canConvertExpr: Sourcelike = ["t == typeof(", csType, ")"];
this.emitCanConvert(canConvertExpr);
this.ensureBlankLine();
this.emitReadJson(() => {
// FIXME: It's unsatisfying that we need this. The reason is that we not
// only match T, but also T?. If we didn't, then the T in T? would not be
// deserialized with our converter but with the default one. Can we check
// whether the type is a nullable?
// FIXME: This could duplicate one of the cases handled below in
// `emitDecodeTransformer`.
// if (haveNullable && !(targetType instanceof UnionType)) {
// this.emitLine("if (reader.TokenType == JsonTokenType.Null) return null;");
// }
const allHandled = this.emitDecodeTransformer(xfer, targetType, v => this.emitLine("return ", v, ";"));
if (!allHandled) {
this.emitThrow(['"Cannot unmarshal type ', csType, '"']);
}
}, csType);
this.ensureBlankLine();
this.emitWriteJson("value", () => {
// FIXME: See above.
// if (haveNullable && !(targetType instanceof UnionType)) {
// this.emitLine("if (value == null)");
// this.emitBlock(() => {
// this.emitLine("writer.WriteNullValue();");
// this.emitLine("return;");
// });
// }
const allHandled = this.emitDecodeTransformer(xfer, targetType, v =>
this.emitLine("return ", v, ";")
);
if (!allHandled) {
this.emitThrow(['"Cannot unmarshal type ', csType, '"']);
}
}, csType);
this.ensureBlankLine();
this.emitWriteJson(
"value",
() => {
// FIXME: See above.
// if (haveNullable && !(targetType instanceof UnionType)) {
// this.emitLine("if (value == null)");
// this.emitBlock(() => {
// this.emitLine("writer.WriteNullValue();");
// this.emitLine("return;");
// });
// }
const allHandled = this.emitTransformer("value", reverse.transformer, reverse.targetType, () => this.emitLine("return;")
const allHandled = this.emitTransformer("value", reverse.transformer, reverse.targetType, () =>
this.emitLine("return;")
);
if (!allHandled) {
this.emitThrow(['"Cannot marshal type ', csType, '"']);
}
},
csType
);
if (!allHandled) {
this.emitThrow(['"Cannot marshal type ', csType, '"']);
}
}, csType);
this.ensureBlankLine();
this.emitLine("public static readonly ", converterName, " Singleton = new ", converterName, "();");
});
this.ensureBlankLine();
this.emitLine("public static readonly ", converterName, " Singleton = new ", converterName, "();");
}
);
}
protected emitRequiredHelpers(): void {
@ -2299,4 +2331,4 @@ internal class IsoDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
protected needNamespace(): boolean {
return this._needNamespaces;
}
}
}

View File

@ -26,7 +26,10 @@ import { RenderContext } from "../Renderer";
export const elmOptions = {
justTypes: new BooleanOption("just-types", "Plain types only", false),
useList: new EnumOption("array-type", "Use Array or List", [["array", false], ["list", true]]),
useList: new EnumOption("array-type", "Use Array or List", [
["array", false],
["list", true]
]),
// FIXME: Do this via a configurable named eventually.
moduleName: new StringOption("module", "Generated module name", "NAME", "QuickType")
};
@ -639,7 +642,9 @@ import Dict exposing (Dict, map, toList)`);
this.ensureBlankLine();
this.emitLine("-- decoders and encoders");
this.forEachTopLevel("leading-and-interposing", (t: Type, topLevelName: Name) => this.emitTopLevelFunctions(t, topLevelName));
this.forEachTopLevel("leading-and-interposing", (t: Type, topLevelName: Name) =>
this.emitTopLevelFunctions(t, topLevelName)
);
this.forEachNamedType(
"leading-and-interposing",
(c: ClassType, className: Name) => this.emitClassFunctions(c, className),

View File

@ -10,7 +10,7 @@ import {
combineWords,
firstUpperWordStyle,
allUpperWordStyle,
camelCase,
camelCase
} from "../support/Strings";
import { assert, defined } from "../support/Support";
import { StringOption, BooleanOption, Option, OptionValues, getOptionValues } from "../RendererOptions";
@ -24,7 +24,7 @@ export const goOptions = {
justTypes: new BooleanOption("just-types", "Plain types only", false),
justTypesAndPackage: new BooleanOption("just-types-and-package", "Plain types with package only", false),
packageName: new StringOption("package", "Generated package name", "NAME", "main"),
multiFileOutput: new BooleanOption("multi-file-output", "Renders each top-level object in its own Go file", false),
multiFileOutput: new BooleanOption("multi-file-output", "Renders each top-level object in its own Go file", false)
};
export class GoTargetLanguage extends TargetLanguage {
@ -121,7 +121,7 @@ export class GoRenderer extends ConvenienceRenderer {
const unmarshalName = new DependencyName(
namingFunction,
topLevelName.order,
(lookup) => `unmarshal_${lookup(topLevelName)}`
lookup => `unmarshal_${lookup(topLevelName)}`
);
this._topLevelUnmarshalNames.set(topLevelName, unmarshalName);
return [unmarshalName];
@ -186,15 +186,15 @@ export class GoRenderer extends ConvenienceRenderer {
private goType(t: Type, withIssues: boolean = false): Sourcelike {
return matchType<Sourcelike>(
t,
(_anyType) => maybeAnnotated(withIssues, anyTypeIssueAnnotation, "interface{}"),
(_nullType) => maybeAnnotated(withIssues, nullTypeIssueAnnotation, "interface{}"),
(_boolType) => "bool",
(_integerType) => "int64",
(_doubleType) => "float64",
(_stringType) => "string",
(arrayType) => ["[]", this.goType(arrayType.items, withIssues)],
(classType) => this.nameForNamedType(classType),
(mapType) => {
_anyType => maybeAnnotated(withIssues, anyTypeIssueAnnotation, "interface{}"),
_nullType => maybeAnnotated(withIssues, nullTypeIssueAnnotation, "interface{}"),
_boolType => "bool",
_integerType => "int64",
_doubleType => "float64",
_stringType => "string",
arrayType => ["[]", this.goType(arrayType.items, withIssues)],
classType => this.nameForNamedType(classType),
mapType => {
let valueSource: Sourcelike;
const v = mapType.values;
if (v instanceof UnionType && nullableFromUnion(v) === null) {
@ -204,8 +204,8 @@ export class GoRenderer extends ConvenienceRenderer {
}
return ["map[string]", valueSource];
},
(enumType) => this.nameForNamedType(enumType),
(unionType) => {
enumType => this.nameForNamedType(enumType),
unionType => {
const nullable = nullableFromUnion(unionType);
if (nullable !== null) return this.nullableGoType(nullable, withIssues);
return this.nameForNamedType(unionType);
@ -260,12 +260,17 @@ export class GoRenderer extends ConvenienceRenderer {
let columns: Sourcelike[][] = [];
this.forEachClassProperty(c, "none", (name, jsonName, p) => {
const description = this.descriptionForClassProperty(c, jsonName);
const docStrings = description !== undefined && description.length > 0 ? description.map(d => "// " + d) : [];
const docStrings =
description !== undefined && description.length > 0 ? description.map(d => "// " + d) : [];
const goType = this.propertyGoType(p);
const omitEmpty = canOmitEmpty(p) ? ",omitempty" : [];
docStrings.forEach(doc => columns.push([doc]));
columns.push([[name, " "], [goType, " "], ['`json:"', stringEscape(jsonName), omitEmpty, '"`']]);
columns.push([
[name, " "],
[goType, " "],
['`json:"', stringEscape(jsonName), omitEmpty, '"`']
]);
});
this.emitDescription(this.descriptionForType(c));
this.emitStruct(className, columns);
@ -348,7 +353,7 @@ export class GoRenderer extends ConvenienceRenderer {
this.emitLine("var c ", goType);
});
const args = makeArgs(
(fn) => ["&x.", fn],
fn => ["&x.", fn],
(isClass, fn) => {
if (isClass) {
return "true, &c";
@ -371,7 +376,7 @@ export class GoRenderer extends ConvenienceRenderer {
this.ensureBlankLine();
this.emitFunc(["(x *", unionName, ") MarshalJSON() ([]byte, error)"], () => {
const args = makeArgs(
(fn) => ["x.", fn],
fn => ["x.", fn],
(_, fn) => ["x.", fn, " != nil, x.", fn]
);
this.emitLine("return marshalUnion(", args, ")");
@ -552,7 +557,7 @@ func marshalUnion(pi *int64, pf *float64, pb *bool, ps *string, haveArray bool,
this.forEachTopLevel(
"leading-and-interposing",
(t, name) => this.emitTopLevel(t, name),
(t) =>
t =>
!(this._options.justTypes || this._options.justTypesAndPackage) ||
this.namedTypeToNameForTopLevel(t) === undefined
);

View File

@ -15,7 +15,7 @@ import {
combineWords,
firstUpperWordStyle,
allLowerWordStyle,
allUpperWordStyle,
allUpperWordStyle
} from "../support/Strings";
import { Sourcelike, MultiWord, singleWord, multiWord, parenIfNeeded } from "../Source";
import { RenderContext } from "../Renderer";
@ -24,9 +24,9 @@ export const haskellOptions = {
justTypes: new BooleanOption("just-types", "Plain types only", false),
useList: new EnumOption("array-type", "Use Array or List", [
["array", false],
["list", true],
["list", true]
]),
moduleName: new StringOption("module", "Generated module name", "NAME", "QuickType"),
moduleName: new StringOption("module", "Generated module name", "NAME", "QuickType")
};
export class HaskellTargetLanguage extends TargetLanguage {
@ -106,10 +106,10 @@ const forbiddenNames = [
"Object",
"Result",
"Series",
"Error",
"Error"
];
const legalizeName = legalizeCharacters((cp) => isAscii(cp) && isLetterOrUnderscoreOrDigit(cp));
const legalizeName = legalizeCharacters(cp => isAscii(cp) && isLetterOrUnderscoreOrDigit(cp));
function haskellNameStyle(original: string, upper: boolean): string {
const words = splitIntoWords(original);
@ -125,8 +125,8 @@ function haskellNameStyle(original: string, upper: boolean): string {
);
}
const upperNamingFunction = funPrefixNamer("upper", (n) => haskellNameStyle(n, true));
const lowerNamingFunction = funPrefixNamer("lower", (n) => haskellNameStyle(n, false));
const upperNamingFunction = funPrefixNamer("upper", n => haskellNameStyle(n, true));
const lowerNamingFunction = funPrefixNamer("lower", n => haskellNameStyle(n, false));
export class HaskellRenderer extends ConvenienceRenderer {
constructor(
@ -194,22 +194,22 @@ export class HaskellRenderer extends ConvenienceRenderer {
private haskellType(t: Type, noOptional: boolean = false): MultiWord {
return matchType<MultiWord>(
t,
(_anyType) => multiWord(" ", "Maybe", "Text"),
(_nullType) => multiWord(" ", "Maybe", "Text"),
(_boolType) => singleWord("Bool"),
(_integerType) => singleWord("Int"),
(_doubleType) => singleWord("Float"),
(_stringType) => singleWord("Text"),
(arrayType) => {
_anyType => multiWord(" ", "Maybe", "Text"),
_nullType => multiWord(" ", "Maybe", "Text"),
_boolType => singleWord("Bool"),
_integerType => singleWord("Int"),
_doubleType => singleWord("Float"),
_stringType => singleWord("Text"),
arrayType => {
if (this._options.useList) {
return multiWord("", "[", parenIfNeeded(this.haskellType(arrayType.items)), "]");
}
return multiWord(" ", "Vector", parenIfNeeded(this.haskellType(arrayType.items)));
},
(classType) => singleWord(this.nameForNamedType(classType)),
(mapType) => multiWord(" ", "HashMap Text", parenIfNeeded(this.haskellType(mapType.values))),
(enumType) => singleWord(this.nameForNamedType(enumType)),
(unionType) => {
classType => singleWord(this.nameForNamedType(classType)),
mapType => multiWord(" ", "HashMap Text", parenIfNeeded(this.haskellType(mapType.values))),
enumType => singleWord(this.nameForNamedType(enumType)),
unionType => {
const nullable = nullableFromUnion(unionType);
if (nullable !== null) {
const nullableType = this.haskellType(nullable);
@ -285,7 +285,7 @@ export class HaskellRenderer extends ConvenienceRenderer {
this.emitLine("data ", enumName);
this.indent(() => {
let onFirst = true;
this.forEachEnumCase(e, "none", (name) => {
this.forEachEnumCase(e, "none", name => {
const equalsOrPipe = onFirst ? "=" : "|";
this.emitLine(equalsOrPipe, " ", name, enumName);
onFirst = false;
@ -327,7 +327,7 @@ export class HaskellRenderer extends ConvenienceRenderer {
private emitClassEncoderInstance(c: ClassType, className: Name): void {
let classProperties: Array<Name | string> = [];
this.forEachClassProperty(c, "none", (name) => {
this.forEachClassProperty(c, "none", name => {
classProperties.push(" ");
classProperties.push(name);
classProperties.push(className);
@ -343,7 +343,7 @@ export class HaskellRenderer extends ConvenienceRenderer {
this.emitLine("object");
let onFirst = true;
this.forEachClassProperty(c, "none", (name, jsonName) => {
this.emitLine(onFirst ? "[ " : ", ", "\"", stringEscape(jsonName), "\" .= ", name, className);
this.emitLine(onFirst ? "[ " : ", ", '"', stringEscape(jsonName), '" .= ', name, className);
onFirst = false;
});
if (onFirst) {
@ -367,13 +367,12 @@ export class HaskellRenderer extends ConvenienceRenderer {
let onFirst = true;
this.forEachClassProperty(c, "none", (_, jsonName, p) => {
const operator = p.isOptional ? ".:?" : ".:";
this.emitLine(onFirst ? "<$> " : "<*> ", "v ", operator, " \"", stringEscape(jsonName), "\"");
this.emitLine(onFirst ? "<$> " : "<*> ", "v ", operator, ' "', stringEscape(jsonName), '"');
onFirst = false;
});
});
}
});
}
private emitClassFunctions(c: ClassType, className: Name): void {
@ -386,7 +385,7 @@ export class HaskellRenderer extends ConvenienceRenderer {
this.emitLine("instance ToJSON ", enumName, " where");
this.indent(() => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine("toJSON ", name, enumName, " = \"", stringEscape(jsonName), "\"");
this.emitLine("toJSON ", name, enumName, ' = "', stringEscape(jsonName), '"');
});
});
}
@ -394,12 +393,12 @@ export class HaskellRenderer extends ConvenienceRenderer {
private emitEnumDecoderInstance(e: EnumType, enumName: Name): void {
this.emitLine("instance FromJSON ", enumName, " where");
this.indent(() => {
this.emitLine("parseJSON = withText \"", enumName, "\" parseText");
this.emitLine('parseJSON = withText "', enumName, '" parseText');
this.indent(() => {
this.emitLine("where");
this.indent(() => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine("parseText \"", stringEscape(jsonName), "\" = return ", name, enumName);
this.emitLine('parseText "', stringEscape(jsonName), '" = return ', name, enumName);
});
});
});
@ -432,7 +431,13 @@ export class HaskellRenderer extends ConvenienceRenderer {
if (t.kind === "null") {
this.emitLine("parseJSON Null = return ", constructor);
} else {
this.emitLine("parseJSON xs@(", this.encoderNameForType(t).source, " _) = (fmap ", constructor, " . parseJSON) xs");
this.emitLine(
"parseJSON xs@(",
this.encoderNameForType(t).source,
" _) = (fmap ",
constructor,
" . parseJSON) xs"
);
}
});
});
@ -502,7 +507,9 @@ import Data.Text (Text)`);
(u: UnionType, unionName: Name) => this.emitUnionDefinition(u, unionName)
);
this.forEachTopLevel("leading-and-interposing", (_: Type, topLevelName: Name) => this.emitTopLevelFunctions(topLevelName));
this.forEachTopLevel("leading-and-interposing", (_: Type, topLevelName: Name) =>
this.emitTopLevelFunctions(topLevelName)
);
this.forEachNamedType(
"leading-and-interposing",

View File

@ -1,1411 +1,1448 @@
import { anyTypeIssueAnnotation, nullTypeIssueAnnotation } from "../Annotation";
import { ConvenienceRenderer, ForbiddenWordsInfo } from "../ConvenienceRenderer";
import { DependencyName, funPrefixNamer, Name, Namer } from "../Naming";
import { RenderContext } from "../Renderer";
import { BooleanOption, EnumOption, getOptionValues, Option, OptionValues, StringOption } from "../RendererOptions";
import { maybeAnnotated, Sourcelike } from "../Source";
import { acronymOption, acronymStyle, AcronymStyleOptions } from "../support/Acronyms";
import {
allLowerWordStyle,
allUpperWordStyle,
capitalize,
combineWords,
escapeNonPrintableMapper,
firstUpperWordStyle,
isAscii,
isDigit,
isLetter,
splitIntoWords,
standardUnicodeHexEscape,
utf16ConcatMap,
utf16LegalizeCharacters,
} from "../support/Strings";
import { assert, assertNever, defined, panic } from "../support/Support";
import { TargetLanguage } from "../TargetLanguage";
import { ArrayType, ClassProperty, ClassType, EnumType, MapType, Type, TypeKind, UnionType } from "../Type";
import { directlyReachableSingleNamedType, matchType, nullableFromUnion, removeNullFromUnion } from "../TypeUtils";
import { StringTypeMapping, TransformedStringTypeKind, PrimitiveStringTypeKind } from "..";
export const javaOptions = {
useList: new EnumOption(
"array-type",
"Use T[] or List<T>",
[
["array", false],
["list", true],
],
"array"
),
justTypes: new BooleanOption("just-types", "Plain types only", false),
dateTimeProvider: new EnumOption(
"datetime-provider",
"Date time provider type",
[
["java8", "java8"],
["legacy", "legacy"]
],
"java8"
),
acronymStyle: acronymOption(AcronymStyleOptions.Pascal),
// FIXME: Do this via a configurable named eventually.
packageName: new StringOption("package", "Generated package name", "NAME", "io.quicktype"),
lombok: new BooleanOption("lombok", "Use lombok", false, "primary"),
lombokCopyAnnotations: new BooleanOption("lombok-copy-annotations", "Copy accessor annotations", true, "secondary"),
};
export class JavaTargetLanguage extends TargetLanguage {
constructor() {
super("Java", ["java"], "java");
}
protected getOptions(): Option<any>[] {
return [
javaOptions.useList,
javaOptions.justTypes,
javaOptions.dateTimeProvider,
javaOptions.acronymStyle,
javaOptions.packageName,
javaOptions.lombok,
javaOptions.lombokCopyAnnotations,
];
}
get supportsUnionsWithBothNumberTypes(): boolean {
return true;
}
protected makeRenderer(renderContext: RenderContext, untypedOptionValues: { [name: string]: any }): JavaRenderer {
const options = getOptionValues(javaOptions, untypedOptionValues);
if (options.justTypes) {
return new JavaRenderer(this, renderContext, options);
}
return new JacksonRenderer(this, renderContext, options);
}
get stringTypeMapping(): StringTypeMapping {
const mapping: Map<TransformedStringTypeKind, PrimitiveStringTypeKind> = new Map();
mapping.set("date", "date");
mapping.set("time", "time");
mapping.set("date-time", "date-time");
mapping.set("uuid", "uuid");
return mapping;
}
}
const javaKeywords = [
"Object",
"Class",
"System",
"Long",
"Double",
"Boolean",
"String",
"List",
"Map",
"UUID",
"Exception",
"IOException",
"Override",
"abstract",
"continue",
"for",
"new",
"switch",
"assert",
"default",
"goto",
"package",
"synchronized",
"boolean",
"do",
"if",
"private",
"this",
"break",
"double",
"implements",
"protected",
"throw",
"byte",
"else",
"import",
"public",
"throws",
"case",
"enum",
"instanceof",
"return",
"transient",
"catch",
"extends",
"int",
"short",
"try",
"char",
"final",
"interface",
"static",
"void",
"class",
"finally",
"long",
"strictfp",
"volatile",
"const",
"float",
"native",
"super",
"while",
"null",
"false",
"true",
];
export const stringEscape = utf16ConcatMap(escapeNonPrintableMapper(isAscii, standardUnicodeHexEscape));
function isStartCharacter(codePoint: number): boolean {
if (codePoint === 0x5f) return true; // underscore
return isAscii(codePoint) && isLetter(codePoint);
}
function isPartCharacter(codePoint: number): boolean {
return isStartCharacter(codePoint) || (isAscii(codePoint) && isDigit(codePoint));
}
const legalizeName = utf16LegalizeCharacters(isPartCharacter);
export function javaNameStyle(
startWithUpper: boolean,
upperUnderscore: boolean,
original: string,
acronymsStyle: (s: string) => string = allUpperWordStyle
): string {
const words = splitIntoWords(original);
return combineWords(
words,
legalizeName,
upperUnderscore ? allUpperWordStyle : startWithUpper ? firstUpperWordStyle : allLowerWordStyle,
upperUnderscore ? allUpperWordStyle : firstUpperWordStyle,
upperUnderscore || startWithUpper ? allUpperWordStyle : allLowerWordStyle,
acronymsStyle,
upperUnderscore ? "_" : "",
isStartCharacter
);
}
abstract class JavaDateTimeProvider {
constructor(protected readonly _renderer: JavaRenderer, protected readonly _className: string) { }
abstract keywords: string[];
abstract dateTimeImports: string[];
abstract dateImports: string[];
abstract timeImports: string[];
abstract converterImports: string[];
abstract dateTimeType: string;
abstract dateType: string;
abstract timeType: string;
abstract dateTimeJacksonAnnotations: string[];
abstract dateJacksonAnnotations: string[];
abstract timeJacksonAnnotations: string[];
abstract emitDateTimeConverters(): void;
public shouldEmitDateTimeConverter: boolean = true;
public shouldEmitTimeConverter: boolean = true;
public shouldEmitDateConverter: boolean = true;
abstract convertStringToDateTime(variable: Sourcelike): Sourcelike;
abstract convertStringToTime(variable: Sourcelike): Sourcelike;
abstract convertStringToDate(variable: Sourcelike): Sourcelike;
abstract convertDateTimeToString(variable: Sourcelike): Sourcelike;
abstract convertTimeToString(variable: Sourcelike): Sourcelike;
abstract convertDateToString(variable: Sourcelike): Sourcelike;
}
class Java8DateTimeProvider extends JavaDateTimeProvider {
keywords = [
"LocalDate",
"OffsetDateTime",
"OffsetTime",
"ZoneOffset",
"ZonedDateTime",
"DateTimeFormatter",
"DateTimeFormatterBuilder",
"ChronoField"
];
dateTimeImports: string[] = ["java.time.OffsetDateTime"];
dateImports: string[] = ["java.time.LocalDate"];
timeImports: string[] = ["java.time.OffsetTime"];
converterImports: string[] = [
"java.time.LocalDate",
"java.time.OffsetDateTime",
"java.time.OffsetTime",
"java.time.ZoneOffset",
"java.time.ZonedDateTime",
"java.time.format.DateTimeFormatter",
"java.time.format.DateTimeFormatterBuilder",
"java.time.temporal.ChronoField"
];
dateTimeType: string = "OffsetDateTime";
dateType: string = "LocalDate";
timeType: string = "OffsetTime";
dateTimeJacksonAnnotations: string[] = [];
dateJacksonAnnotations: string[] = [];
timeJacksonAnnotations: string[] = [];
emitDateTimeConverters(): void {
this._renderer.ensureBlankLine();
this._renderer.emitLine("private static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()");
this._renderer.indent(() => this._renderer.indent(() => {
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_DATE_TIME)");
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_OFFSET_DATE_TIME)");
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_INSTANT)");
this._renderer.emitLine('.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SX"))');
this._renderer.emitLine('.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"))');
this._renderer.emitLine('.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))');
this._renderer.emitLine(".toFormatter()");
this._renderer.emitLine(".withZone(ZoneOffset.UTC);");
}));
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static OffsetDateTime parseDateTimeString(String str)",
() => {
this._renderer.emitLine("return ZonedDateTime.from(Converter.DATE_TIME_FORMATTER.parse(str)).toOffsetDateTime();");
}
);
this._renderer.ensureBlankLine();
this._renderer.emitLine("private static final DateTimeFormatter TIME_FORMATTER = new DateTimeFormatterBuilder()");
this._renderer.indent(() => this._renderer.indent(() => {
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_TIME)");
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_OFFSET_TIME)");
this._renderer.emitLine(".parseDefaulting(ChronoField.YEAR, 2020)");
this._renderer.emitLine(".parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)");
this._renderer.emitLine(".parseDefaulting(ChronoField.DAY_OF_MONTH, 1)");
this._renderer.emitLine(".toFormatter()");
this._renderer.emitLine(".withZone(ZoneOffset.UTC);");
}));
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static OffsetTime parseTimeString(String str)",
() => {
this._renderer.emitLine("return ZonedDateTime.from(Converter.TIME_FORMATTER.parse(str)).toOffsetDateTime().toOffsetTime();");
}
);
}
convertStringToDateTime(variable: Sourcelike): Sourcelike {
return [this._className, ".parseDateTimeString(", variable, ")"];
}
convertStringToTime(variable: Sourcelike): Sourcelike {
return [this._className, ".parseTimeString(", variable, ")"];
}
convertStringToDate(variable: Sourcelike): Sourcelike {
return ["LocalDate.parse(", variable, ")"];
}
convertDateTimeToString(variable: Sourcelike): Sourcelike {
return [variable, ".format(java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME)"];
}
convertTimeToString(variable: Sourcelike): Sourcelike {
return [variable, ".format(java.time.format.DateTimeFormatter.ISO_OFFSET_TIME)"];
}
convertDateToString(variable: Sourcelike): Sourcelike {
return [variable, ".format(java.time.format.DateTimeFormatter.ISO_DATE)"];
}
}
class JavaLegacyDateTimeProvider extends JavaDateTimeProvider {
keywords = [
"SimpleDateFormat",
"Date",
];
dateTimeImports: string[] = ["java.util.Date"];
dateImports: string[] = ["java.util.Date"];
timeImports: string[] = ["java.util.Date"];
converterImports: string[] = [
"java.util.Date",
"java.text.SimpleDateFormat",
];
dateTimeType: string = "Date";
dateType: string = "Date";
timeType: string = "Date";
dateTimeJacksonAnnotations: string[] = ['@JsonFormat(pattern = "yyyy-MM-dd\'T\'HH:mm:ssX", timezone = "UTC")'];
dateJacksonAnnotations: string[] = ['@JsonFormat(pattern = "yyyy-MM-dd")'];
timeJacksonAnnotations: string[] = ['@JsonFormat(pattern = "HH:mm:ssX", timezone = "UTC")'];
emitDateTimeConverters(): void {
this._renderer.ensureBlankLine();
this._renderer.emitLine("private static final String[] DATE_TIME_FORMATS = {");
this._renderer.indent(() => this._renderer.indent(() => {
this._renderer.emitLine('"yyyy-MM-dd\'T\'HH:mm:ss.SX",');
this._renderer.emitLine('"yyyy-MM-dd\'T\'HH:mm:ss.S",');
this._renderer.emitLine('"yyyy-MM-dd\'T\'HH:mm:ssX",');
this._renderer.emitLine('"yyyy-MM-dd\'T\'HH:mm:ss",');
this._renderer.emitLine('"yyyy-MM-dd HH:mm:ss.SX",');
this._renderer.emitLine('"yyyy-MM-dd HH:mm:ss.S",');
this._renderer.emitLine('"yyyy-MM-dd HH:mm:ssX",');
this._renderer.emitLine('"yyyy-MM-dd HH:mm:ss",');
this._renderer.emitLine('"HH:mm:ss.SZ",');
this._renderer.emitLine('"HH:mm:ss.S",');
this._renderer.emitLine('"HH:mm:ssZ",');
this._renderer.emitLine('"HH:mm:ss",');
this._renderer.emitLine('"yyyy-MM-dd",');
}));
this._renderer.emitLine("};");
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static Date parseAllDateTimeString(String str)",
() => {
this._renderer.emitBlock("for (String format : DATE_TIME_FORMATS)", () => {
this._renderer.emitIgnoredTryCatchBlock(() => {
this._renderer.emitLine("return new SimpleDateFormat(format).parse(str);");
});
});
this._renderer.emitLine("return null;");
}
);
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static String serializeDateTime(Date datetime)",
() => {
this._renderer.emitLine('return new SimpleDateFormat("yyyy-MM-dd\'T\'hh:mm:ssZ").format(datetime);');
}
);
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static String serializeDate(Date datetime)",
() => {
this._renderer.emitLine('return new SimpleDateFormat("yyyy-MM-dd").format(datetime);');
}
);
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static String serializeTime(Date datetime)",
() => {
this._renderer.emitLine('return new SimpleDateFormat("hh:mm:ssZ").format(datetime);');
}
);
}
shouldEmitTimeConverter = false;
shouldEmitDateConverter = false;
convertStringToDateTime(variable: Sourcelike): Sourcelike {
return [this._className, ".parseAllDateTimeString(", variable, ")"];
}
convertStringToTime(variable: Sourcelike): Sourcelike {
return [this._className, ".parseAllDateTimeString(", variable, ")"];
}
convertStringToDate(variable: Sourcelike): Sourcelike {
return [this._className, ".parseAllDateTimeString(", variable, ")"];
}
convertDateTimeToString(variable: Sourcelike): Sourcelike {
return [this._className, ".serializeDateTime(", variable, ")"];
}
convertTimeToString(variable: Sourcelike): Sourcelike {
return [this._className, ".serializeTime(", variable, ")"];
}
convertDateToString(variable: Sourcelike): Sourcelike {
return [this._className, ".serializeDate(", variable, ")"];
}
}
export class JavaRenderer extends ConvenienceRenderer {
private _currentFilename: string | undefined;
private readonly _gettersAndSettersForPropertyName = new Map<Name, [Name, Name]>();
private _haveEmittedLeadingComments = false;
protected readonly _dateTimeProvider: JavaDateTimeProvider;
protected readonly _converterClassname: string = "Converter";
protected readonly _converterKeywords: string[] = [];
constructor(
targetLanguage: TargetLanguage,
renderContext: RenderContext,
protected readonly _options: OptionValues<typeof javaOptions>,
) {
super(targetLanguage, renderContext);
switch (_options.dateTimeProvider) {
default:
case "java8":
this._dateTimeProvider = new Java8DateTimeProvider(this, this._converterClassname);
break;
case "legacy":
this._dateTimeProvider = new JavaLegacyDateTimeProvider(this, this._converterClassname);
break;
}
}
protected forbiddenNamesForGlobalNamespace(): string[] {
const keywords = [...javaKeywords, ...this._converterKeywords, this._converterClassname, ...this._dateTimeProvider.keywords];
return keywords;
}
protected forbiddenForObjectProperties(_c: ClassType, _className: Name): ForbiddenWordsInfo {
return { names: [], includeGlobalForbidden: true };
}
protected makeNamedTypeNamer(): Namer {
return this.getNameStyling("typeNamingFunction");
}
protected namerForObjectProperty(): Namer {
return this.getNameStyling("propertyNamingFunction");
}
protected makeUnionMemberNamer(): Namer {
return this.getNameStyling("propertyNamingFunction");
}
protected makeEnumCaseNamer(): Namer {
return this.getNameStyling("enumCaseNamingFunction");
}
protected unionNeedsName(u: UnionType): boolean {
return nullableFromUnion(u) === null;
}
protected namedTypeToNameForTopLevel(type: Type): Type | undefined {
// If the top-level type doesn't contain any classes or unions
// we have to define a class just for the `FromJson` method, in
// emitFromJsonForTopLevel.
return directlyReachableSingleNamedType(type);
}
protected makeNamesForPropertyGetterAndSetter(
_c: ClassType,
_className: Name,
_p: ClassProperty,
_jsonName: string,
name: Name
): [Name, Name] {
const getterName = new DependencyName(
this.getNameStyling("propertyNamingFunction"),
name.order,
(lookup) => `get_${lookup(name)}`
);
const setterName = new DependencyName(
this.getNameStyling("propertyNamingFunction"),
name.order,
(lookup) => `set_${lookup(name)}`
);
return [getterName, setterName];
}
protected makePropertyDependencyNames(
c: ClassType,
className: Name,
p: ClassProperty,
jsonName: string,
name: Name
): Name[] {
const getterAndSetterNames = this.makeNamesForPropertyGetterAndSetter(c, className, p, jsonName, name);
this._gettersAndSettersForPropertyName.set(name, getterAndSetterNames);
return getterAndSetterNames;
}
private getNameStyling(convention: string): Namer {
const styling: { [key: string]: Namer } = {
typeNamingFunction: funPrefixNamer("types", (n) =>
javaNameStyle(true, false, n, acronymStyle(this._options.acronymStyle))
),
propertyNamingFunction: funPrefixNamer("properties", (n) =>
javaNameStyle(false, false, n, acronymStyle(this._options.acronymStyle))
),
enumCaseNamingFunction: funPrefixNamer("enum-cases", (n) =>
javaNameStyle(true, true, n, acronymStyle(this._options.acronymStyle))
),
};
return styling[convention];
}
protected fieldOrMethodName(methodName: string, topLevelName: Name): Sourcelike {
if (this.topLevels.size === 1) {
return methodName;
}
return [topLevelName, capitalize(methodName)];
}
protected methodName(prefix: string, suffix: string, topLevelName: Name): Sourcelike {
if (this.topLevels.size === 1) {
return [prefix, suffix];
}
return [prefix, topLevelName, suffix];
}
protected decoderName(topLevelName: Name): Sourcelike {
return this.fieldOrMethodName("fromJsonString", topLevelName);
}
protected encoderName(topLevelName: Name): Sourcelike {
return this.fieldOrMethodName("toJsonString", topLevelName);
}
protected readerGetterName(topLevelName: Name): Sourcelike {
return this.methodName("get", "ObjectReader", topLevelName);
}
protected writerGetterName(topLevelName: Name): Sourcelike {
return this.methodName("get", "ObjectWriter", topLevelName);
}
protected startFile(basename: Sourcelike): void {
assert(this._currentFilename === undefined, "Previous file wasn't finished");
// FIXME: The filenames should actually be Sourcelikes, too
this._currentFilename = `${this.sourcelikeToString(basename)}.java`;
// FIXME: Why is this necessary?
this.ensureBlankLine();
if (!this._haveEmittedLeadingComments && this.leadingComments !== undefined) {
this.emitCommentLines(this.leadingComments);
this.ensureBlankLine();
this._haveEmittedLeadingComments = true;
}
}
protected finishFile(): void {
super.finishFile(defined(this._currentFilename));
this._currentFilename = undefined;
}
protected emitPackageAndImports(imports: string[]): void {
this.emitLine("package ", this._options.packageName, ";");
this.ensureBlankLine();
for (const pkg of imports) {
this.emitLine("import ", pkg, ";");
}
}
protected emitFileHeader(fileName: Sourcelike, imports: string[]): void {
this.startFile(fileName);
this.emitPackageAndImports(imports);
this.ensureBlankLine();
}
public emitDescriptionBlock(lines: Sourcelike[]): void {
this.emitCommentLines(lines, " * ", "/**", " */");
}
public emitBlock(line: Sourcelike, f: () => void): void {
this.emitLine(line, " {");
this.indent(f);
this.emitLine("}");
}
public emitTryCatch(main: () => void, handler: () => void, exception: string = "Exception") {
this.emitLine("try {");
this.indent(main);
this.emitLine("} catch (", exception, " ex) {");
this.indent(handler);
this.emitLine("}");
}
public emitIgnoredTryCatchBlock(f: () => void) {
this.emitTryCatch(f, () => this.emitLine("// Ignored"));
}
protected javaType(reference: boolean, t: Type, withIssues: boolean = false): Sourcelike {
return matchType<Sourcelike>(
t,
(_anyType) => maybeAnnotated(withIssues, anyTypeIssueAnnotation, "Object"),
(_nullType) => maybeAnnotated(withIssues, nullTypeIssueAnnotation, "Object"),
(_boolType) => (reference ? "Boolean" : "boolean"),
(_integerType) => (reference ? "Long" : "long"),
(_doubleType) => (reference ? "Double" : "double"),
(_stringType) => "String",
(arrayType) => {
if (this._options.useList) {
return ["List<", this.javaType(true, arrayType.items, withIssues), ">"];
} else {
return [this.javaType(false, arrayType.items, withIssues), "[]"];
}
},
(classType) => this.nameForNamedType(classType),
(mapType) => ["Map<String, ", this.javaType(true, mapType.values, withIssues), ">"],
(enumType) => this.nameForNamedType(enumType),
(unionType) => {
const nullable = nullableFromUnion(unionType);
if (nullable !== null) return this.javaType(true, nullable, withIssues);
return this.nameForNamedType(unionType);
},
(transformedStringType) => {
if (transformedStringType.kind === "time") {
return this._dateTimeProvider.timeType;
}
if (transformedStringType.kind === "date") {
return this._dateTimeProvider.dateType;
}
if (transformedStringType.kind === "date-time") {
return this._dateTimeProvider.dateTimeType;
}
if (transformedStringType.kind === "uuid") {
return "UUID";
}
return "String";
}
);
}
protected javaImport(t: Type): string[] {
return matchType<string[]>(
t,
(_anyType) => [],
(_nullType) => [],
(_boolType) => [],
(_integerType) => [],
(_doubleType) => [],
(_stringType) => [],
(arrayType) => {
if (this._options.useList) {
return [...this.javaImport(arrayType.items), "java.util.List"];
} else {
return [...this.javaImport(arrayType.items)];
}
},
(_classType) => [],
(mapType) => [...this.javaImport(mapType.values), "java.util.Map"],
(_enumType) => [],
(unionType) => {
const imports: string[] = [];
unionType.members.forEach((type) => this.javaImport(type).forEach((imp) => imports.push(imp)));
return imports;
},
(transformedStringType) => {
if (transformedStringType.kind === "time") {
return this._dateTimeProvider.timeImports;
}
if (transformedStringType.kind === "date") {
return this._dateTimeProvider.dateImports;
}
if (transformedStringType.kind === "date-time") {
return this._dateTimeProvider.dateTimeImports;
}
if (transformedStringType.kind === "uuid") {
return ["java.util.UUID"];
}
return [];
}
);
}
protected javaTypeWithoutGenerics(reference: boolean, t: Type): Sourcelike {
if (t instanceof ArrayType) {
if (this._options.useList) {
return ["List"];
} else {
return [this.javaTypeWithoutGenerics(false, t.items), "[]"];
}
} else if (t instanceof MapType) {
return "Map";
} else if (t instanceof UnionType) {
const nullable = nullableFromUnion(t);
if (nullable !== null) return this.javaTypeWithoutGenerics(true, nullable);
return this.nameForNamedType(t);
} else {
return this.javaType(reference, t);
}
}
protected emitClassAttributes(_c: ClassType, _className: Name): void {
if (this._options.lombok) {
this.emitLine("@lombok.Data");
}
}
protected annotationsForAccessor(
_c: ClassType,
_className: Name,
_propertyName: Name,
_jsonName: string,
_p: ClassProperty,
_isSetter: boolean
): string[] {
return [];
}
protected importsForType(t: ClassType | UnionType | EnumType): string[] {
if (t instanceof ClassType) {
return [];
}
if (t instanceof UnionType) {
return ["java.io.IOException"];
}
if (t instanceof EnumType) {
return ["java.io.IOException"];
}
return assertNever(t);
}
protected importsForClass(c: ClassType): string[] {
const imports: string[] = [];
this.forEachClassProperty(c, "none", (_name, _jsonName, p) => {
this.javaImport(p.type).forEach((imp) => imports.push(imp));
});
imports.sort();
return [...new Set(imports)];
}
protected importsForUnionMembers(u: UnionType): string[] {
const imports: string[] = [];
const [, nonNulls] = removeNullFromUnion(u);
this.forEachUnionMember(u, nonNulls, "none", null, (_fieldName, t) => {
this.javaImport(t).forEach((imp) => imports.push(imp));
});
imports.sort();
return [...new Set(imports)];
}
protected emitClassDefinition(c: ClassType, className: Name): void {
let imports = [...this.importsForType(c), ...this.importsForClass(c)];
this.emitFileHeader(className, imports);
this.emitDescription(this.descriptionForType(c));
this.emitClassAttributes(c, className);
this.emitBlock(["public class ", className], () => {
this.forEachClassProperty(c, "none", (name, jsonName, p) => {
if (this._options.lombok && this._options.lombokCopyAnnotations) {
const getter = this.annotationsForAccessor(c, className, name, jsonName, p, false);
const setter = this.annotationsForAccessor(c, className, name, jsonName, p, true);
if (getter.length !== 0) {
this.emitLine("@lombok.Getter(onMethod_ = {" + getter.join(", ") + "})");
}
if (setter.length !== 0) {
this.emitLine("@lombok.Setter(onMethod_ = {" + setter.join(", ") + "})");
}
}
this.emitLine("private ", this.javaType(false, p.type, true), " ", name, ";");
});
if (!this._options.lombok) {
this.forEachClassProperty(c, "leading-and-interposing", (name, jsonName, p) => {
this.emitDescription(this.descriptionForClassProperty(c, jsonName));
const [getterName, setterName] = defined(this._gettersAndSettersForPropertyName.get(name));
const rendered = this.javaType(false, p.type);
this.annotationsForAccessor(c, className, name, jsonName, p, false)
.forEach(annotation => this.emitLine(annotation));
this.emitLine("public ", rendered, " ", getterName, "() { return ", name, "; }");
this.annotationsForAccessor(c, className, name, jsonName, p, true)
.forEach(annotation => this.emitLine(annotation));
this.emitLine("public void ", setterName, "(", rendered, " value) { this.", name, " = value; }");
});
}
});
this.finishFile();
}
protected unionField(
u: UnionType,
t: Type,
withIssues: boolean = false
): { fieldType: Sourcelike; fieldName: Sourcelike } {
const fieldType = this.javaType(true, t, withIssues);
// FIXME: "Value" should be part of the name.
const fieldName = [this.nameForUnionMember(u, t), "Value"];
return { fieldType, fieldName };
}
protected emitUnionAttributes(_u: UnionType, _unionName: Name): void {
// empty
}
protected emitUnionSerializer(_u: UnionType, _unionName: Name): void {
// empty
}
protected emitUnionDefinition(u: UnionType, unionName: Name): void {
const imports = [...this.importsForType(u), ...this.importsForUnionMembers(u)];
this.emitFileHeader(unionName, imports);
this.emitDescription(this.descriptionForType(u));
const [, nonNulls] = removeNullFromUnion(u);
this.emitUnionAttributes(u, unionName);
this.emitBlock(["public class ", unionName], () => {
for (const t of nonNulls) {
const { fieldType, fieldName } = this.unionField(u, t, true);
this.emitLine("public ", fieldType, " ", fieldName, ";");
}
this.emitUnionSerializer(u, unionName);
});
this.finishFile();
}
protected emitEnumSerializationAttributes(_e: EnumType) {
// Empty
}
protected emitEnumDeserializationAttributes(_e: EnumType) {
// Empty
}
protected emitEnumDefinition(e: EnumType, enumName: Name): void {
this.emitFileHeader(enumName, this.importsForType(e));
this.emitDescription(this.descriptionForType(e));
const caseNames: Sourcelike[] = [];
this.forEachEnumCase(e, "none", (name) => {
if (caseNames.length > 0) caseNames.push(", ");
caseNames.push(name);
});
caseNames.push(";");
this.emitBlock(["public enum ", enumName], () => {
this.emitLine(caseNames);
this.ensureBlankLine();
this.emitEnumSerializationAttributes(e);
this.emitBlock("public String toValue()", () => {
this.emitLine("switch (this) {");
this.indent(() => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine("case ", name, ': return "', stringEscape(jsonName), '";');
});
});
this.emitLine("}");
this.emitLine("return null;");
});
this.ensureBlankLine();
this.emitEnumDeserializationAttributes(e);
this.emitBlock(["public static ", enumName, " forValue(String value) throws IOException"], () => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine('if (value.equals("', stringEscape(jsonName), '")) return ', name, ";");
});
this.emitLine('throw new IOException("Cannot deserialize ', enumName, '");');
});
});
this.finishFile();
}
protected emitSourceStructure(): void {
this.forEachNamedType(
"leading-and-interposing",
(c: ClassType, n: Name) => this.emitClassDefinition(c, n),
(e, n) => this.emitEnumDefinition(e, n),
(u, n) => this.emitUnionDefinition(u, n)
);
}
}
export class JacksonRenderer extends JavaRenderer {
constructor(
targetLanguage: TargetLanguage,
renderContext: RenderContext,
options: OptionValues<typeof javaOptions>,
) {
super(targetLanguage, renderContext, options);
}
protected readonly _converterKeywords: string[] = [
"JsonProperty",
"JsonDeserialize",
"JsonDeserializer",
"JsonSerialize",
"JsonSerializer",
"JsonParser",
"JsonProcessingException",
"DeserializationContext",
"SerializerProvider",
];
protected emitClassAttributes(c: ClassType, _className: Name): void {
if (c.getProperties().size === 0)
this.emitLine("@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.NONE)");
super.emitClassAttributes(c, _className);
}
protected annotationsForAccessor(
_c: ClassType,
_className: Name,
_propertyName: Name,
jsonName: string,
p: ClassProperty,
_isSetter: boolean
): string[] {
const superAnnotations = super.annotationsForAccessor(_c, _className, _propertyName, jsonName, p, _isSetter);
const annotations: string[] = [
('@JsonProperty("' + stringEscape(jsonName) + '")')
];
switch (p.type.kind) {
case "date-time":
this._dateTimeProvider.dateTimeJacksonAnnotations.forEach(annotation => annotations.push(annotation));
break;
case "date":
this._dateTimeProvider.dateJacksonAnnotations.forEach(annotation => annotations.push(annotation));
break;
case "time":
this._dateTimeProvider.timeJacksonAnnotations.forEach(annotation => annotations.push(annotation));
break;
default:
break;
}
return [...superAnnotations, ...annotations];
}
protected importsForType(t: ClassType | UnionType | EnumType): string[] {
if (t instanceof ClassType) {
const imports = super.importsForType(t);
imports.push("com.fasterxml.jackson.annotation.*");
return imports;
}
if (t instanceof UnionType) {
const imports = super.importsForType(t);
imports.push("java.io.IOException",
"com.fasterxml.jackson.core.*",
"com.fasterxml.jackson.databind.*",
"com.fasterxml.jackson.databind.annotation.*"
);
if (this._options.useList) {
imports.push("com.fasterxml.jackson.core.type.*");
}
return imports;
}
if (t instanceof EnumType) {
const imports = super.importsForType(t);
imports.push("com.fasterxml.jackson.annotation.*");
return imports;
}
return assertNever(t);
}
protected emitUnionAttributes(_u: UnionType, unionName: Name): void {
this.emitLine("@JsonDeserialize(using = ", unionName, ".Deserializer.class)");
this.emitLine("@JsonSerialize(using = ", unionName, ".Serializer.class)");
}
protected emitUnionSerializer(u: UnionType, unionName: Name): void {
const stringBasedObjects: TypeKind[] = ["uuid", "time", "date", "date-time"];
const tokenCase = (tokenType: string): void => {
this.emitLine("case ", tokenType, ":");
};
const emitNullDeserializer = (): void => {
this.indent(() => {
tokenCase("VALUE_NULL");
this.indent(() => this.emitLine("break;"));
});
};
const emitDeserializerCodeForStringObjects = (
fieldName: Sourcelike,
kind: TypeKind,
parseFrom: string
): void => {
switch (kind) {
case "date":
this.emitLine("value.", fieldName, " = ", this._dateTimeProvider.convertStringToDate(parseFrom), ";");
break;
case "time":
this.emitLine("value.", fieldName, " = ", this._dateTimeProvider.convertStringToTime(parseFrom), ";");
break;
case "date-time":
this.emitLine("value.", fieldName, " = ", this._dateTimeProvider.convertStringToDateTime(parseFrom), ";");
break;
case "uuid":
this.emitLine("value.", fieldName, " = UUID.fromString(", parseFrom, ");");
break;
default:
return panic("Requested type isnt an object!");
}
};
const emitDeserializeType = (t: Type, variableFieldName: string = ""): void => {
const { fieldName } = this.unionField(u, t);
const rendered = this.javaTypeWithoutGenerics(true, t);
if (this._options.useList && t instanceof ArrayType) {
this.emitLine(
"value.",
fieldName,
" = jsonParser.readValueAs(new TypeReference<",
rendered,
">() {});"
);
} else if (stringBasedObjects.some((stringBasedTypeKind) => t.kind === stringBasedTypeKind)) {
emitDeserializerCodeForStringObjects(fieldName, t.kind, variableFieldName);
} else if (t.kind === "string") {
this.emitLine("value.", fieldName, " = ", variableFieldName, ";");
} else if (t.kind === "enum") {
const { fieldType } = this.unionField(u, t, true);
this.emitLine("value.", fieldName, " = ", fieldType, ".forValue(", variableFieldName, ");");
} else {
this.emitLine("value.", fieldName, " = jsonParser.readValueAs(", rendered, ".class);");
}
};
const emitDeserializer = (tokenTypes: string[], kind: TypeKind): void => {
const t = u.findMember(kind);
if (t === undefined) return;
this.indent(() => {
for (const tokenType of tokenTypes) {
tokenCase(tokenType);
}
this.indent(() => {
emitDeserializeType(t);
this.emitLine("break;");
});
});
};
const emitStringDeserializer = () => {
const enumType = u.findMember("enum");
const stringType = u.findMember("string");
if (
stringBasedObjects.every((kind) => u.findMember(kind) === undefined) &&
stringType === undefined &&
enumType === undefined
)
return;
this.indent(() => {
tokenCase("VALUE_STRING");
this.indent(() => {
const fromVariable = "string";
this.emitLine("String " + fromVariable + " = jsonParser.readValueAs(String.class);");
stringBasedObjects.forEach((kind) => {
const type = u.findMember(kind);
if (type !== undefined) {
this.emitIgnoredTryCatchBlock(() => {
emitDeserializeType(type, fromVariable);
});
}
});
if (enumType !== undefined) {
this.emitIgnoredTryCatchBlock(() => {
emitDeserializeType(enumType, fromVariable);
});
}
// String should be the last one if exists, because it cannot fail, unlike the parsers.
if (stringType !== undefined) {
emitDeserializeType(stringType, fromVariable);
}
this.emitLine("break;");
});
});
};
const emitNumberDeserializer = (): void => {
const integerType = u.findMember("integer");
const doubleType = u.findMember("double");
if (doubleType === undefined && integerType === undefined) return;
this.indent(() => {
tokenCase("VALUE_NUMBER_INT");
if (integerType !== undefined) {
this.indent(() => {
emitDeserializeType(integerType);
this.emitLine("break;");
});
}
if (doubleType !== undefined) {
tokenCase("VALUE_NUMBER_FLOAT");
this.indent(() => {
emitDeserializeType(doubleType);
this.emitLine("break;");
});
}
});
};
const customObjectSerializer: TypeKind[] = ["time", "date", "date-time"];
const serializerCodeForType = (type: Type, fieldName: Sourcelike): Sourcelike => {
switch (type.kind) {
case "date":
return this._dateTimeProvider.convertDateToString(fieldName);
case "time":
return this._dateTimeProvider.convertTimeToString(fieldName);
case "date-time":
return this._dateTimeProvider.convertDateTimeToString(fieldName);
default:
return panic("Requested type doesn't have custom serializer code!");
}
};
const emitSerializeType = (t: Type): void => {
let { fieldName } = this.unionField(u, t, true);
this.emitBlock(["if (obj.", fieldName, " != null)"], () => {
if (customObjectSerializer.some((customSerializerType) => t.kind === customSerializerType)) {
this.emitLine("jsonGenerator.writeObject(", serializerCodeForType(t, ["obj.", fieldName]), ");");
} else {
this.emitLine("jsonGenerator.writeObject(obj.", fieldName, ");");
}
this.emitLine("return;");
});
};
const [maybeNull, nonNulls] = removeNullFromUnion(u);
this.ensureBlankLine();
this.emitBlock(["static class Deserializer extends JsonDeserializer<", unionName, ">"], () => {
this.emitLine("@Override");
this.emitBlock(
[
"public ",
unionName,
" deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException",
],
() => {
this.emitLine(unionName, " value = new ", unionName, "();");
this.emitLine("switch (jsonParser.currentToken()) {");
if (maybeNull !== null) emitNullDeserializer();
emitNumberDeserializer();
emitDeserializer(["VALUE_TRUE", "VALUE_FALSE"], "bool");
emitStringDeserializer();
emitDeserializer(["START_ARRAY"], "array");
emitDeserializer(["START_OBJECT"], "class");
emitDeserializer(["START_OBJECT"], "map");
this.indent(() =>
this.emitLine('default: throw new IOException("Cannot deserialize ', unionName, '");')
);
this.emitLine("}");
this.emitLine("return value;");
}
);
});
this.ensureBlankLine();
this.emitBlock(["static class Serializer extends JsonSerializer<", unionName, ">"], () => {
this.emitLine("@Override");
this.emitBlock(
[
"public void serialize(",
unionName,
" obj, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException",
],
() => {
for (const t of nonNulls) {
emitSerializeType(t);
}
if (maybeNull !== null) {
this.emitLine("jsonGenerator.writeNull();");
} else {
this.emitLine('throw new IOException("', unionName, ' must not be null");');
}
}
);
});
}
protected emitEnumSerializationAttributes(_e: EnumType) {
this.emitLine("@JsonValue");
}
protected emitEnumDeserializationAttributes(_e: EnumType) {
this.emitLine("@JsonCreator");
}
protected emitOffsetDateTimeConverterModule(): void {
this.emitLine("SimpleModule module = new SimpleModule();");
if (this._dateTimeProvider.shouldEmitDateTimeConverter) {
this.emitLine("module.addDeserializer(", this._dateTimeProvider.dateTimeType, ".class, new JsonDeserializer<", this._dateTimeProvider.dateTimeType, ">() {");
this.indent(() => {
this.emitLine("@Override");
this.emitBlock(
[
"public ",
this._dateTimeProvider.dateTimeType,
" deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) ",
"throws IOException, JsonProcessingException"
],
() => {
this.emitLine("String value = jsonParser.getText();");
this.emitLine("return ", this._dateTimeProvider.convertStringToDateTime("value"), ";");
}
);
});
this.emitLine("});");
}
if (!this._dateTimeProvider.shouldEmitTimeConverter) {
this.emitLine("module.addDeserializer(", this._dateTimeProvider.timeType, ".class, new JsonDeserializer<", this._dateTimeProvider.timeType, ">() {");
this.indent(() => {
this.emitLine("@Override");
this.emitBlock(
[
"public ",
this._dateTimeProvider.timeType,
" deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) ",
"throws IOException, JsonProcessingException"
],
() => {
this.emitLine("String value = jsonParser.getText();");
this.emitLine("return ", this._dateTimeProvider.convertStringToTime("value"), ";");
}
);
});
this.emitLine("});");
}
if (!this._dateTimeProvider.shouldEmitDateConverter) {
this.emitLine("module.addDeserializer(", this._dateTimeProvider.dateType, ".class, new JsonDeserializer<", this._dateTimeProvider.dateType, ">() {");
this.indent(() => {
this.emitLine("@Override");
this.emitBlock(
[
"public ",
this._dateTimeProvider.dateType,
" deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) ",
"throws IOException, JsonProcessingException"
],
() => {
this.emitLine("String value = jsonParser.getText();");
this.emitLine("return ", this._dateTimeProvider.convertStringToDate("value"), ";");
}
);
});
this.emitLine("});");
}
this.emitLine("mapper.registerModule(module);");
}
protected emitConverterClass(): void {
this.startFile(this._converterClassname);
this.emitCommentLines([
"To use this code, add the following Maven dependency to your project:",
"",
this._options.lombok ? " org.projectlombok : lombok : 1.18.2" : "",
" com.fasterxml.jackson.core : jackson-databind : 2.9.0",
this._options.dateTimeProvider === "java8" ? " com.fasterxml.jackson.datatype : jackson-datatype-jsr310 : 2.9.0" : "",
"",
"Import this package:",
"",
]);
this.emitLine("// import ", this._options.packageName, ".Converter;");
this.emitMultiline(`//
// Then you can deserialize a JSON string with
//`);
this.forEachTopLevel("none", (t, name) => {
this.emitLine(
"// ",
this.javaType(false, t),
" data = Converter.",
this.decoderName(name),
"(jsonString);"
);
});
this.ensureBlankLine();
const imports = [
"java.io.IOException",
"com.fasterxml.jackson.databind.*",
"com.fasterxml.jackson.databind.module.SimpleModule",
"com.fasterxml.jackson.core.JsonParser",
"com.fasterxml.jackson.core.JsonProcessingException",
"java.util.*",
].concat(this._dateTimeProvider.converterImports);
this.emitPackageAndImports(imports);
this.ensureBlankLine();
this.emitBlock(["public class Converter"], () => {
this.emitLine("// Date-time helpers");
this._dateTimeProvider.emitDateTimeConverters();
this.emitLine("// Serialize/deserialize helpers");
this.forEachTopLevel("leading-and-interposing", (topLevelType, topLevelName) => {
const topLevelTypeRendered = this.javaType(false, topLevelType);
this.emitBlock(
[
"public static ",
topLevelTypeRendered,
" ",
this.decoderName(topLevelName),
"(String json) throws IOException",
],
() => {
this.emitLine("return ", this.readerGetterName(topLevelName), "().readValue(json);");
}
);
this.ensureBlankLine();
this.emitBlock(
[
"public static String ",
this.encoderName(topLevelName),
"(",
topLevelTypeRendered,
" obj) throws JsonProcessingException",
],
() => {
this.emitLine("return ", this.writerGetterName(topLevelName), "().writeValueAsString(obj);");
}
);
});
this.forEachTopLevel("leading-and-interposing", (topLevelType, topLevelName) => {
const readerName = this.fieldOrMethodName("reader", topLevelName);
const writerName = this.fieldOrMethodName("writer", topLevelName);
this.emitLine("private static ObjectReader ", readerName, ";");
this.emitLine("private static ObjectWriter ", writerName, ";");
this.ensureBlankLine();
this.emitBlock(
["private static void ", this.methodName("instantiate", "Mapper", topLevelName), "()"],
() => {
const renderedForClass = this.javaTypeWithoutGenerics(false, topLevelType);
this.emitLine("ObjectMapper mapper = new ObjectMapper();");
this.emitLine("mapper.findAndRegisterModules();");
this.emitLine("mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);");
this.emitLine("mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);");
this.emitOffsetDateTimeConverterModule();
this.emitLine(readerName, " = mapper.readerFor(", renderedForClass, ".class);");
this.emitLine(writerName, " = mapper.writerFor(", renderedForClass, ".class);");
}
);
this.ensureBlankLine();
this.emitBlock(["private static ObjectReader ", this.readerGetterName(topLevelName), "()"], () => {
this.emitLine(
"if (",
readerName,
" == null) ",
this.methodName("instantiate", "Mapper", topLevelName),
"();"
);
this.emitLine("return ", readerName, ";");
});
this.ensureBlankLine();
this.emitBlock(["private static ObjectWriter ", this.writerGetterName(topLevelName), "()"], () => {
this.emitLine(
"if (",
writerName,
" == null) ",
this.methodName("instantiate", "Mapper", topLevelName),
"();"
);
this.emitLine("return ", writerName, ";");
});
});
});
this.finishFile();
}
protected emitSourceStructure(): void {
this.emitConverterClass();
super.emitSourceStructure();
}
}
import { anyTypeIssueAnnotation, nullTypeIssueAnnotation } from "../Annotation";
import { ConvenienceRenderer, ForbiddenWordsInfo } from "../ConvenienceRenderer";
import { DependencyName, funPrefixNamer, Name, Namer } from "../Naming";
import { RenderContext } from "../Renderer";
import { BooleanOption, EnumOption, getOptionValues, Option, OptionValues, StringOption } from "../RendererOptions";
import { maybeAnnotated, Sourcelike } from "../Source";
import { acronymOption, acronymStyle, AcronymStyleOptions } from "../support/Acronyms";
import {
allLowerWordStyle,
allUpperWordStyle,
capitalize,
combineWords,
escapeNonPrintableMapper,
firstUpperWordStyle,
isAscii,
isDigit,
isLetter,
splitIntoWords,
standardUnicodeHexEscape,
utf16ConcatMap,
utf16LegalizeCharacters
} from "../support/Strings";
import { assert, assertNever, defined, panic } from "../support/Support";
import { TargetLanguage } from "../TargetLanguage";
import { ArrayType, ClassProperty, ClassType, EnumType, MapType, Type, TypeKind, UnionType } from "../Type";
import { directlyReachableSingleNamedType, matchType, nullableFromUnion, removeNullFromUnion } from "../TypeUtils";
import { StringTypeMapping, TransformedStringTypeKind, PrimitiveStringTypeKind } from "..";
export const javaOptions = {
useList: new EnumOption(
"array-type",
"Use T[] or List<T>",
[
["array", false],
["list", true]
],
"array"
),
justTypes: new BooleanOption("just-types", "Plain types only", false),
dateTimeProvider: new EnumOption(
"datetime-provider",
"Date time provider type",
[
["java8", "java8"],
["legacy", "legacy"]
],
"java8"
),
acronymStyle: acronymOption(AcronymStyleOptions.Pascal),
// FIXME: Do this via a configurable named eventually.
packageName: new StringOption("package", "Generated package name", "NAME", "io.quicktype"),
lombok: new BooleanOption("lombok", "Use lombok", false, "primary"),
lombokCopyAnnotations: new BooleanOption("lombok-copy-annotations", "Copy accessor annotations", true, "secondary")
};
export class JavaTargetLanguage extends TargetLanguage {
constructor() {
super("Java", ["java"], "java");
}
protected getOptions(): Option<any>[] {
return [
javaOptions.useList,
javaOptions.justTypes,
javaOptions.dateTimeProvider,
javaOptions.acronymStyle,
javaOptions.packageName,
javaOptions.lombok,
javaOptions.lombokCopyAnnotations
];
}
get supportsUnionsWithBothNumberTypes(): boolean {
return true;
}
protected makeRenderer(renderContext: RenderContext, untypedOptionValues: { [name: string]: any }): JavaRenderer {
const options = getOptionValues(javaOptions, untypedOptionValues);
if (options.justTypes) {
return new JavaRenderer(this, renderContext, options);
}
return new JacksonRenderer(this, renderContext, options);
}
get stringTypeMapping(): StringTypeMapping {
const mapping: Map<TransformedStringTypeKind, PrimitiveStringTypeKind> = new Map();
mapping.set("date", "date");
mapping.set("time", "time");
mapping.set("date-time", "date-time");
mapping.set("uuid", "uuid");
return mapping;
}
}
const javaKeywords = [
"Object",
"Class",
"System",
"Long",
"Double",
"Boolean",
"String",
"List",
"Map",
"UUID",
"Exception",
"IOException",
"Override",
"abstract",
"continue",
"for",
"new",
"switch",
"assert",
"default",
"goto",
"package",
"synchronized",
"boolean",
"do",
"if",
"private",
"this",
"break",
"double",
"implements",
"protected",
"throw",
"byte",
"else",
"import",
"public",
"throws",
"case",
"enum",
"instanceof",
"return",
"transient",
"catch",
"extends",
"int",
"short",
"try",
"char",
"final",
"interface",
"static",
"void",
"class",
"finally",
"long",
"strictfp",
"volatile",
"const",
"float",
"native",
"super",
"while",
"null",
"false",
"true"
];
export const stringEscape = utf16ConcatMap(escapeNonPrintableMapper(isAscii, standardUnicodeHexEscape));
function isStartCharacter(codePoint: number): boolean {
if (codePoint === 0x5f) return true; // underscore
return isAscii(codePoint) && isLetter(codePoint);
}
function isPartCharacter(codePoint: number): boolean {
return isStartCharacter(codePoint) || (isAscii(codePoint) && isDigit(codePoint));
}
const legalizeName = utf16LegalizeCharacters(isPartCharacter);
export function javaNameStyle(
startWithUpper: boolean,
upperUnderscore: boolean,
original: string,
acronymsStyle: (s: string) => string = allUpperWordStyle
): string {
const words = splitIntoWords(original);
return combineWords(
words,
legalizeName,
upperUnderscore ? allUpperWordStyle : startWithUpper ? firstUpperWordStyle : allLowerWordStyle,
upperUnderscore ? allUpperWordStyle : firstUpperWordStyle,
upperUnderscore || startWithUpper ? allUpperWordStyle : allLowerWordStyle,
acronymsStyle,
upperUnderscore ? "_" : "",
isStartCharacter
);
}
abstract class JavaDateTimeProvider {
constructor(protected readonly _renderer: JavaRenderer, protected readonly _className: string) {}
abstract keywords: string[];
abstract dateTimeImports: string[];
abstract dateImports: string[];
abstract timeImports: string[];
abstract converterImports: string[];
abstract dateTimeType: string;
abstract dateType: string;
abstract timeType: string;
abstract dateTimeJacksonAnnotations: string[];
abstract dateJacksonAnnotations: string[];
abstract timeJacksonAnnotations: string[];
abstract emitDateTimeConverters(): void;
public shouldEmitDateTimeConverter: boolean = true;
public shouldEmitTimeConverter: boolean = true;
public shouldEmitDateConverter: boolean = true;
abstract convertStringToDateTime(variable: Sourcelike): Sourcelike;
abstract convertStringToTime(variable: Sourcelike): Sourcelike;
abstract convertStringToDate(variable: Sourcelike): Sourcelike;
abstract convertDateTimeToString(variable: Sourcelike): Sourcelike;
abstract convertTimeToString(variable: Sourcelike): Sourcelike;
abstract convertDateToString(variable: Sourcelike): Sourcelike;
}
class Java8DateTimeProvider extends JavaDateTimeProvider {
keywords = [
"LocalDate",
"OffsetDateTime",
"OffsetTime",
"ZoneOffset",
"ZonedDateTime",
"DateTimeFormatter",
"DateTimeFormatterBuilder",
"ChronoField"
];
dateTimeImports: string[] = ["java.time.OffsetDateTime"];
dateImports: string[] = ["java.time.LocalDate"];
timeImports: string[] = ["java.time.OffsetTime"];
converterImports: string[] = [
"java.time.LocalDate",
"java.time.OffsetDateTime",
"java.time.OffsetTime",
"java.time.ZoneOffset",
"java.time.ZonedDateTime",
"java.time.format.DateTimeFormatter",
"java.time.format.DateTimeFormatterBuilder",
"java.time.temporal.ChronoField"
];
dateTimeType: string = "OffsetDateTime";
dateType: string = "LocalDate";
timeType: string = "OffsetTime";
dateTimeJacksonAnnotations: string[] = [];
dateJacksonAnnotations: string[] = [];
timeJacksonAnnotations: string[] = [];
emitDateTimeConverters(): void {
this._renderer.ensureBlankLine();
this._renderer.emitLine(
"private static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()"
);
this._renderer.indent(() =>
this._renderer.indent(() => {
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_DATE_TIME)");
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_OFFSET_DATE_TIME)");
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_INSTANT)");
this._renderer.emitLine('.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SX"))');
this._renderer.emitLine('.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"))');
this._renderer.emitLine('.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))');
this._renderer.emitLine(".toFormatter()");
this._renderer.emitLine(".withZone(ZoneOffset.UTC);");
})
);
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static OffsetDateTime parseDateTimeString(String str)", () => {
this._renderer.emitLine(
"return ZonedDateTime.from(Converter.DATE_TIME_FORMATTER.parse(str)).toOffsetDateTime();"
);
});
this._renderer.ensureBlankLine();
this._renderer.emitLine(
"private static final DateTimeFormatter TIME_FORMATTER = new DateTimeFormatterBuilder()"
);
this._renderer.indent(() =>
this._renderer.indent(() => {
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_TIME)");
this._renderer.emitLine(".appendOptional(DateTimeFormatter.ISO_OFFSET_TIME)");
this._renderer.emitLine(".parseDefaulting(ChronoField.YEAR, 2020)");
this._renderer.emitLine(".parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)");
this._renderer.emitLine(".parseDefaulting(ChronoField.DAY_OF_MONTH, 1)");
this._renderer.emitLine(".toFormatter()");
this._renderer.emitLine(".withZone(ZoneOffset.UTC);");
})
);
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static OffsetTime parseTimeString(String str)", () => {
this._renderer.emitLine(
"return ZonedDateTime.from(Converter.TIME_FORMATTER.parse(str)).toOffsetDateTime().toOffsetTime();"
);
});
}
convertStringToDateTime(variable: Sourcelike): Sourcelike {
return [this._className, ".parseDateTimeString(", variable, ")"];
}
convertStringToTime(variable: Sourcelike): Sourcelike {
return [this._className, ".parseTimeString(", variable, ")"];
}
convertStringToDate(variable: Sourcelike): Sourcelike {
return ["LocalDate.parse(", variable, ")"];
}
convertDateTimeToString(variable: Sourcelike): Sourcelike {
return [variable, ".format(java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME)"];
}
convertTimeToString(variable: Sourcelike): Sourcelike {
return [variable, ".format(java.time.format.DateTimeFormatter.ISO_OFFSET_TIME)"];
}
convertDateToString(variable: Sourcelike): Sourcelike {
return [variable, ".format(java.time.format.DateTimeFormatter.ISO_DATE)"];
}
}
class JavaLegacyDateTimeProvider extends JavaDateTimeProvider {
keywords = ["SimpleDateFormat", "Date"];
dateTimeImports: string[] = ["java.util.Date"];
dateImports: string[] = ["java.util.Date"];
timeImports: string[] = ["java.util.Date"];
converterImports: string[] = ["java.util.Date", "java.text.SimpleDateFormat"];
dateTimeType: string = "Date";
dateType: string = "Date";
timeType: string = "Date";
dateTimeJacksonAnnotations: string[] = ['@JsonFormat(pattern = "yyyy-MM-dd\'T\'HH:mm:ssX", timezone = "UTC")'];
dateJacksonAnnotations: string[] = ['@JsonFormat(pattern = "yyyy-MM-dd")'];
timeJacksonAnnotations: string[] = ['@JsonFormat(pattern = "HH:mm:ssX", timezone = "UTC")'];
emitDateTimeConverters(): void {
this._renderer.ensureBlankLine();
this._renderer.emitLine("private static final String[] DATE_TIME_FORMATS = {");
this._renderer.indent(() =>
this._renderer.indent(() => {
this._renderer.emitLine("\"yyyy-MM-dd'T'HH:mm:ss.SX\",");
this._renderer.emitLine("\"yyyy-MM-dd'T'HH:mm:ss.S\",");
this._renderer.emitLine("\"yyyy-MM-dd'T'HH:mm:ssX\",");
this._renderer.emitLine("\"yyyy-MM-dd'T'HH:mm:ss\",");
this._renderer.emitLine('"yyyy-MM-dd HH:mm:ss.SX",');
this._renderer.emitLine('"yyyy-MM-dd HH:mm:ss.S",');
this._renderer.emitLine('"yyyy-MM-dd HH:mm:ssX",');
this._renderer.emitLine('"yyyy-MM-dd HH:mm:ss",');
this._renderer.emitLine('"HH:mm:ss.SZ",');
this._renderer.emitLine('"HH:mm:ss.S",');
this._renderer.emitLine('"HH:mm:ssZ",');
this._renderer.emitLine('"HH:mm:ss",');
this._renderer.emitLine('"yyyy-MM-dd",');
})
);
this._renderer.emitLine("};");
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static Date parseAllDateTimeString(String str)", () => {
this._renderer.emitBlock("for (String format : DATE_TIME_FORMATS)", () => {
this._renderer.emitIgnoredTryCatchBlock(() => {
this._renderer.emitLine("return new SimpleDateFormat(format).parse(str);");
});
});
this._renderer.emitLine("return null;");
});
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static String serializeDateTime(Date datetime)", () => {
this._renderer.emitLine("return new SimpleDateFormat(\"yyyy-MM-dd'T'hh:mm:ssZ\").format(datetime);");
});
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static String serializeDate(Date datetime)", () => {
this._renderer.emitLine('return new SimpleDateFormat("yyyy-MM-dd").format(datetime);');
});
this._renderer.ensureBlankLine();
this._renderer.emitBlock("public static String serializeTime(Date datetime)", () => {
this._renderer.emitLine('return new SimpleDateFormat("hh:mm:ssZ").format(datetime);');
});
}
shouldEmitTimeConverter = false;
shouldEmitDateConverter = false;
convertStringToDateTime(variable: Sourcelike): Sourcelike {
return [this._className, ".parseAllDateTimeString(", variable, ")"];
}
convertStringToTime(variable: Sourcelike): Sourcelike {
return [this._className, ".parseAllDateTimeString(", variable, ")"];
}
convertStringToDate(variable: Sourcelike): Sourcelike {
return [this._className, ".parseAllDateTimeString(", variable, ")"];
}
convertDateTimeToString(variable: Sourcelike): Sourcelike {
return [this._className, ".serializeDateTime(", variable, ")"];
}
convertTimeToString(variable: Sourcelike): Sourcelike {
return [this._className, ".serializeTime(", variable, ")"];
}
convertDateToString(variable: Sourcelike): Sourcelike {
return [this._className, ".serializeDate(", variable, ")"];
}
}
export class JavaRenderer extends ConvenienceRenderer {
private _currentFilename: string | undefined;
private readonly _gettersAndSettersForPropertyName = new Map<Name, [Name, Name]>();
private _haveEmittedLeadingComments = false;
protected readonly _dateTimeProvider: JavaDateTimeProvider;
protected readonly _converterClassname: string = "Converter";
protected readonly _converterKeywords: string[] = [];
constructor(
targetLanguage: TargetLanguage,
renderContext: RenderContext,
protected readonly _options: OptionValues<typeof javaOptions>
) {
super(targetLanguage, renderContext);
switch (_options.dateTimeProvider) {
default:
case "java8":
this._dateTimeProvider = new Java8DateTimeProvider(this, this._converterClassname);
break;
case "legacy":
this._dateTimeProvider = new JavaLegacyDateTimeProvider(this, this._converterClassname);
break;
}
}
protected forbiddenNamesForGlobalNamespace(): string[] {
const keywords = [
...javaKeywords,
...this._converterKeywords,
this._converterClassname,
...this._dateTimeProvider.keywords
];
return keywords;
}
protected forbiddenForObjectProperties(_c: ClassType, _className: Name): ForbiddenWordsInfo {
return { names: [], includeGlobalForbidden: true };
}
protected makeNamedTypeNamer(): Namer {
return this.getNameStyling("typeNamingFunction");
}
protected namerForObjectProperty(): Namer {
return this.getNameStyling("propertyNamingFunction");
}
protected makeUnionMemberNamer(): Namer {
return this.getNameStyling("propertyNamingFunction");
}
protected makeEnumCaseNamer(): Namer {
return this.getNameStyling("enumCaseNamingFunction");
}
protected unionNeedsName(u: UnionType): boolean {
return nullableFromUnion(u) === null;
}
protected namedTypeToNameForTopLevel(type: Type): Type | undefined {
// If the top-level type doesn't contain any classes or unions
// we have to define a class just for the `FromJson` method, in
// emitFromJsonForTopLevel.
return directlyReachableSingleNamedType(type);
}
protected makeNamesForPropertyGetterAndSetter(
_c: ClassType,
_className: Name,
_p: ClassProperty,
_jsonName: string,
name: Name
): [Name, Name] {
const getterName = new DependencyName(
this.getNameStyling("propertyNamingFunction"),
name.order,
lookup => `get_${lookup(name)}`
);
const setterName = new DependencyName(
this.getNameStyling("propertyNamingFunction"),
name.order,
lookup => `set_${lookup(name)}`
);
return [getterName, setterName];
}
protected makePropertyDependencyNames(
c: ClassType,
className: Name,
p: ClassProperty,
jsonName: string,
name: Name
): Name[] {
const getterAndSetterNames = this.makeNamesForPropertyGetterAndSetter(c, className, p, jsonName, name);
this._gettersAndSettersForPropertyName.set(name, getterAndSetterNames);
return getterAndSetterNames;
}
private getNameStyling(convention: string): Namer {
const styling: { [key: string]: Namer } = {
typeNamingFunction: funPrefixNamer("types", n =>
javaNameStyle(true, false, n, acronymStyle(this._options.acronymStyle))
),
propertyNamingFunction: funPrefixNamer("properties", n =>
javaNameStyle(false, false, n, acronymStyle(this._options.acronymStyle))
),
enumCaseNamingFunction: funPrefixNamer("enum-cases", n =>
javaNameStyle(true, true, n, acronymStyle(this._options.acronymStyle))
)
};
return styling[convention];
}
protected fieldOrMethodName(methodName: string, topLevelName: Name): Sourcelike {
if (this.topLevels.size === 1) {
return methodName;
}
return [topLevelName, capitalize(methodName)];
}
protected methodName(prefix: string, suffix: string, topLevelName: Name): Sourcelike {
if (this.topLevels.size === 1) {
return [prefix, suffix];
}
return [prefix, topLevelName, suffix];
}
protected decoderName(topLevelName: Name): Sourcelike {
return this.fieldOrMethodName("fromJsonString", topLevelName);
}
protected encoderName(topLevelName: Name): Sourcelike {
return this.fieldOrMethodName("toJsonString", topLevelName);
}
protected readerGetterName(topLevelName: Name): Sourcelike {
return this.methodName("get", "ObjectReader", topLevelName);
}
protected writerGetterName(topLevelName: Name): Sourcelike {
return this.methodName("get", "ObjectWriter", topLevelName);
}
protected startFile(basename: Sourcelike): void {
assert(this._currentFilename === undefined, "Previous file wasn't finished");
// FIXME: The filenames should actually be Sourcelikes, too
this._currentFilename = `${this.sourcelikeToString(basename)}.java`;
// FIXME: Why is this necessary?
this.ensureBlankLine();
if (!this._haveEmittedLeadingComments && this.leadingComments !== undefined) {
this.emitCommentLines(this.leadingComments);
this.ensureBlankLine();
this._haveEmittedLeadingComments = true;
}
}
protected finishFile(): void {
super.finishFile(defined(this._currentFilename));
this._currentFilename = undefined;
}
protected emitPackageAndImports(imports: string[]): void {
this.emitLine("package ", this._options.packageName, ";");
this.ensureBlankLine();
for (const pkg of imports) {
this.emitLine("import ", pkg, ";");
}
}
protected emitFileHeader(fileName: Sourcelike, imports: string[]): void {
this.startFile(fileName);
this.emitPackageAndImports(imports);
this.ensureBlankLine();
}
public emitDescriptionBlock(lines: Sourcelike[]): void {
this.emitCommentLines(lines, " * ", "/**", " */");
}
public emitBlock(line: Sourcelike, f: () => void): void {
this.emitLine(line, " {");
this.indent(f);
this.emitLine("}");
}
public emitTryCatch(main: () => void, handler: () => void, exception: string = "Exception") {
this.emitLine("try {");
this.indent(main);
this.emitLine("} catch (", exception, " ex) {");
this.indent(handler);
this.emitLine("}");
}
public emitIgnoredTryCatchBlock(f: () => void) {
this.emitTryCatch(f, () => this.emitLine("// Ignored"));
}
protected javaType(reference: boolean, t: Type, withIssues: boolean = false): Sourcelike {
return matchType<Sourcelike>(
t,
_anyType => maybeAnnotated(withIssues, anyTypeIssueAnnotation, "Object"),
_nullType => maybeAnnotated(withIssues, nullTypeIssueAnnotation, "Object"),
_boolType => (reference ? "Boolean" : "boolean"),
_integerType => (reference ? "Long" : "long"),
_doubleType => (reference ? "Double" : "double"),
_stringType => "String",
arrayType => {
if (this._options.useList) {
return ["List<", this.javaType(true, arrayType.items, withIssues), ">"];
} else {
return [this.javaType(false, arrayType.items, withIssues), "[]"];
}
},
classType => this.nameForNamedType(classType),
mapType => ["Map<String, ", this.javaType(true, mapType.values, withIssues), ">"],
enumType => this.nameForNamedType(enumType),
unionType => {
const nullable = nullableFromUnion(unionType);
if (nullable !== null) return this.javaType(true, nullable, withIssues);
return this.nameForNamedType(unionType);
},
transformedStringType => {
if (transformedStringType.kind === "time") {
return this._dateTimeProvider.timeType;
}
if (transformedStringType.kind === "date") {
return this._dateTimeProvider.dateType;
}
if (transformedStringType.kind === "date-time") {
return this._dateTimeProvider.dateTimeType;
}
if (transformedStringType.kind === "uuid") {
return "UUID";
}
return "String";
}
);
}
protected javaImport(t: Type): string[] {
return matchType<string[]>(
t,
_anyType => [],
_nullType => [],
_boolType => [],
_integerType => [],
_doubleType => [],
_stringType => [],
arrayType => {
if (this._options.useList) {
return [...this.javaImport(arrayType.items), "java.util.List"];
} else {
return [...this.javaImport(arrayType.items)];
}
},
_classType => [],
mapType => [...this.javaImport(mapType.values), "java.util.Map"],
_enumType => [],
unionType => {
const imports: string[] = [];
unionType.members.forEach(type => this.javaImport(type).forEach(imp => imports.push(imp)));
return imports;
},
transformedStringType => {
if (transformedStringType.kind === "time") {
return this._dateTimeProvider.timeImports;
}
if (transformedStringType.kind === "date") {
return this._dateTimeProvider.dateImports;
}
if (transformedStringType.kind === "date-time") {
return this._dateTimeProvider.dateTimeImports;
}
if (transformedStringType.kind === "uuid") {
return ["java.util.UUID"];
}
return [];
}
);
}
protected javaTypeWithoutGenerics(reference: boolean, t: Type): Sourcelike {
if (t instanceof ArrayType) {
if (this._options.useList) {
return ["List"];
} else {
return [this.javaTypeWithoutGenerics(false, t.items), "[]"];
}
} else if (t instanceof MapType) {
return "Map";
} else if (t instanceof UnionType) {
const nullable = nullableFromUnion(t);
if (nullable !== null) return this.javaTypeWithoutGenerics(true, nullable);
return this.nameForNamedType(t);
} else {
return this.javaType(reference, t);
}
}
protected emitClassAttributes(_c: ClassType, _className: Name): void {
if (this._options.lombok) {
this.emitLine("@lombok.Data");
}
}
protected annotationsForAccessor(
_c: ClassType,
_className: Name,
_propertyName: Name,
_jsonName: string,
_p: ClassProperty,
_isSetter: boolean
): string[] {
return [];
}
protected importsForType(t: ClassType | UnionType | EnumType): string[] {
if (t instanceof ClassType) {
return [];
}
if (t instanceof UnionType) {
return ["java.io.IOException"];
}
if (t instanceof EnumType) {
return ["java.io.IOException"];
}
return assertNever(t);
}
protected importsForClass(c: ClassType): string[] {
const imports: string[] = [];
this.forEachClassProperty(c, "none", (_name, _jsonName, p) => {
this.javaImport(p.type).forEach(imp => imports.push(imp));
});
imports.sort();
return [...new Set(imports)];
}
protected importsForUnionMembers(u: UnionType): string[] {
const imports: string[] = [];
const [, nonNulls] = removeNullFromUnion(u);
this.forEachUnionMember(u, nonNulls, "none", null, (_fieldName, t) => {
this.javaImport(t).forEach(imp => imports.push(imp));
});
imports.sort();
return [...new Set(imports)];
}
protected emitClassDefinition(c: ClassType, className: Name): void {
let imports = [...this.importsForType(c), ...this.importsForClass(c)];
this.emitFileHeader(className, imports);
this.emitDescription(this.descriptionForType(c));
this.emitClassAttributes(c, className);
this.emitBlock(["public class ", className], () => {
this.forEachClassProperty(c, "none", (name, jsonName, p) => {
if (this._options.lombok && this._options.lombokCopyAnnotations) {
const getter = this.annotationsForAccessor(c, className, name, jsonName, p, false);
const setter = this.annotationsForAccessor(c, className, name, jsonName, p, true);
if (getter.length !== 0) {
this.emitLine("@lombok.Getter(onMethod_ = {" + getter.join(", ") + "})");
}
if (setter.length !== 0) {
this.emitLine("@lombok.Setter(onMethod_ = {" + setter.join(", ") + "})");
}
}
this.emitLine("private ", this.javaType(false, p.type, true), " ", name, ";");
});
if (!this._options.lombok) {
this.forEachClassProperty(c, "leading-and-interposing", (name, jsonName, p) => {
this.emitDescription(this.descriptionForClassProperty(c, jsonName));
const [getterName, setterName] = defined(this._gettersAndSettersForPropertyName.get(name));
const rendered = this.javaType(false, p.type);
this.annotationsForAccessor(c, className, name, jsonName, p, false).forEach(annotation =>
this.emitLine(annotation)
);
this.emitLine("public ", rendered, " ", getterName, "() { return ", name, "; }");
this.annotationsForAccessor(c, className, name, jsonName, p, true).forEach(annotation =>
this.emitLine(annotation)
);
this.emitLine("public void ", setterName, "(", rendered, " value) { this.", name, " = value; }");
});
}
});
this.finishFile();
}
protected unionField(
u: UnionType,
t: Type,
withIssues: boolean = false
): { fieldType: Sourcelike; fieldName: Sourcelike } {
const fieldType = this.javaType(true, t, withIssues);
// FIXME: "Value" should be part of the name.
const fieldName = [this.nameForUnionMember(u, t), "Value"];
return { fieldType, fieldName };
}
protected emitUnionAttributes(_u: UnionType, _unionName: Name): void {
// empty
}
protected emitUnionSerializer(_u: UnionType, _unionName: Name): void {
// empty
}
protected emitUnionDefinition(u: UnionType, unionName: Name): void {
const imports = [...this.importsForType(u), ...this.importsForUnionMembers(u)];
this.emitFileHeader(unionName, imports);
this.emitDescription(this.descriptionForType(u));
const [, nonNulls] = removeNullFromUnion(u);
this.emitUnionAttributes(u, unionName);
this.emitBlock(["public class ", unionName], () => {
for (const t of nonNulls) {
const { fieldType, fieldName } = this.unionField(u, t, true);
this.emitLine("public ", fieldType, " ", fieldName, ";");
}
this.emitUnionSerializer(u, unionName);
});
this.finishFile();
}
protected emitEnumSerializationAttributes(_e: EnumType) {
// Empty
}
protected emitEnumDeserializationAttributes(_e: EnumType) {
// Empty
}
protected emitEnumDefinition(e: EnumType, enumName: Name): void {
this.emitFileHeader(enumName, this.importsForType(e));
this.emitDescription(this.descriptionForType(e));
const caseNames: Sourcelike[] = [];
this.forEachEnumCase(e, "none", name => {
if (caseNames.length > 0) caseNames.push(", ");
caseNames.push(name);
});
caseNames.push(";");
this.emitBlock(["public enum ", enumName], () => {
this.emitLine(caseNames);
this.ensureBlankLine();
this.emitEnumSerializationAttributes(e);
this.emitBlock("public String toValue()", () => {
this.emitLine("switch (this) {");
this.indent(() => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine("case ", name, ': return "', stringEscape(jsonName), '";');
});
});
this.emitLine("}");
this.emitLine("return null;");
});
this.ensureBlankLine();
this.emitEnumDeserializationAttributes(e);
this.emitBlock(["public static ", enumName, " forValue(String value) throws IOException"], () => {
this.forEachEnumCase(e, "none", (name, jsonName) => {
this.emitLine('if (value.equals("', stringEscape(jsonName), '")) return ', name, ";");
});
this.emitLine('throw new IOException("Cannot deserialize ', enumName, '");');
});
});
this.finishFile();
}
protected emitSourceStructure(): void {
this.forEachNamedType(
"leading-and-interposing",
(c: ClassType, n: Name) => this.emitClassDefinition(c, n),
(e, n) => this.emitEnumDefinition(e, n),
(u, n) => this.emitUnionDefinition(u, n)
);
}
}
export class JacksonRenderer extends JavaRenderer {
constructor(
targetLanguage: TargetLanguage,
renderContext: RenderContext,
options: OptionValues<typeof javaOptions>
) {
super(targetLanguage, renderContext, options);
}
protected readonly _converterKeywords: string[] = [
"JsonProperty",
"JsonDeserialize",
"JsonDeserializer",
"JsonSerialize",
"JsonSerializer",
"JsonParser",
"JsonProcessingException",
"DeserializationContext",
"SerializerProvider"
];
protected emitClassAttributes(c: ClassType, _className: Name): void {
if (c.getProperties().size === 0)
this.emitLine("@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.NONE)");
super.emitClassAttributes(c, _className);
}
protected annotationsForAccessor(
_c: ClassType,
_className: Name,
_propertyName: Name,
jsonName: string,
p: ClassProperty,
_isSetter: boolean
): string[] {
const superAnnotations = super.annotationsForAccessor(_c, _className, _propertyName, jsonName, p, _isSetter);
const annotations: string[] = ['@JsonProperty("' + stringEscape(jsonName) + '")'];
switch (p.type.kind) {
case "date-time":
this._dateTimeProvider.dateTimeJacksonAnnotations.forEach(annotation => annotations.push(annotation));
break;
case "date":
this._dateTimeProvider.dateJacksonAnnotations.forEach(annotation => annotations.push(annotation));
break;
case "time":
this._dateTimeProvider.timeJacksonAnnotations.forEach(annotation => annotations.push(annotation));
break;
default:
break;
}
return [...superAnnotations, ...annotations];
}
protected importsForType(t: ClassType | UnionType | EnumType): string[] {
if (t instanceof ClassType) {
const imports = super.importsForType(t);
imports.push("com.fasterxml.jackson.annotation.*");
return imports;
}
if (t instanceof UnionType) {
const imports = super.importsForType(t);
imports.push(
"java.io.IOException",
"com.fasterxml.jackson.core.*",
"com.fasterxml.jackson.databind.*",
"com.fasterxml.jackson.databind.annotation.*"
);
if (this._options.useList) {
imports.push("com.fasterxml.jackson.core.type.*");
}
return imports;
}
if (t instanceof EnumType) {
const imports = super.importsForType(t);
imports.push("com.fasterxml.jackson.annotation.*");
return imports;
}
return assertNever(t);
}
protected emitUnionAttributes(_u: UnionType, unionName: Name): void {
this.emitLine("@JsonDeserialize(using = ", unionName, ".Deserializer.class)");
this.emitLine("@JsonSerialize(using = ", unionName, ".Serializer.class)");
}
protected emitUnionSerializer(u: UnionType, unionName: Name): void {
const stringBasedObjects: TypeKind[] = ["uuid", "time", "date", "date-time"];
const tokenCase = (tokenType: string): void => {
this.emitLine("case ", tokenType, ":");
};
const emitNullDeserializer = (): void => {
this.indent(() => {
tokenCase("VALUE_NULL");
this.indent(() => this.emitLine("break;"));
});
};
const emitDeserializerCodeForStringObjects = (
fieldName: Sourcelike,
kind: TypeKind,
parseFrom: string
): void => {
switch (kind) {
case "date":
this.emitLine(
"value.",
fieldName,
" = ",
this._dateTimeProvider.convertStringToDate(parseFrom),
";"
);
break;
case "time":
this.emitLine(
"value.",
fieldName,
" = ",
this._dateTimeProvider.convertStringToTime(parseFrom),
";"
);
break;
case "date-time":
this.emitLine(
"value.",
fieldName,
" = ",
this._dateTimeProvider.convertStringToDateTime(parseFrom),
";"
);
break;
case "uuid":
this.emitLine("value.", fieldName, " = UUID.fromString(", parseFrom, ");");
break;
default:
return panic("Requested type isnt an object!");
}
};
const emitDeserializeType = (t: Type, variableFieldName: string = ""): void => {
const { fieldName } = this.unionField(u, t);
const rendered = this.javaTypeWithoutGenerics(true, t);
if (this._options.useList && t instanceof ArrayType) {
this.emitLine(
"value.",
fieldName,
" = jsonParser.readValueAs(new TypeReference<",
rendered,
">() {});"
);
} else if (stringBasedObjects.some(stringBasedTypeKind => t.kind === stringBasedTypeKind)) {
emitDeserializerCodeForStringObjects(fieldName, t.kind, variableFieldName);
} else if (t.kind === "string") {
this.emitLine("value.", fieldName, " = ", variableFieldName, ";");
} else if (t.kind === "enum") {
const { fieldType } = this.unionField(u, t, true);
this.emitLine("value.", fieldName, " = ", fieldType, ".forValue(", variableFieldName, ");");
} else {
this.emitLine("value.", fieldName, " = jsonParser.readValueAs(", rendered, ".class);");
}
};
const emitDeserializer = (tokenTypes: string[], kind: TypeKind): void => {
const t = u.findMember(kind);
if (t === undefined) return;
this.indent(() => {
for (const tokenType of tokenTypes) {
tokenCase(tokenType);
}
this.indent(() => {
emitDeserializeType(t);
this.emitLine("break;");
});
});
};
const emitStringDeserializer = () => {
const enumType = u.findMember("enum");
const stringType = u.findMember("string");
if (
stringBasedObjects.every(kind => u.findMember(kind) === undefined) &&
stringType === undefined &&
enumType === undefined
)
return;
this.indent(() => {
tokenCase("VALUE_STRING");
this.indent(() => {
const fromVariable = "string";
this.emitLine("String " + fromVariable + " = jsonParser.readValueAs(String.class);");
stringBasedObjects.forEach(kind => {
const type = u.findMember(kind);
if (type !== undefined) {
this.emitIgnoredTryCatchBlock(() => {
emitDeserializeType(type, fromVariable);
});
}
});
if (enumType !== undefined) {
this.emitIgnoredTryCatchBlock(() => {
emitDeserializeType(enumType, fromVariable);
});
}
// String should be the last one if exists, because it cannot fail, unlike the parsers.
if (stringType !== undefined) {
emitDeserializeType(stringType, fromVariable);
}
this.emitLine("break;");
});
});
};
const emitNumberDeserializer = (): void => {
const integerType = u.findMember("integer");
const doubleType = u.findMember("double");
if (doubleType === undefined && integerType === undefined) return;
this.indent(() => {
tokenCase("VALUE_NUMBER_INT");
if (integerType !== undefined) {
this.indent(() => {
emitDeserializeType(integerType);
this.emitLine("break;");
});
}
if (doubleType !== undefined) {
tokenCase("VALUE_NUMBER_FLOAT");
this.indent(() => {
emitDeserializeType(doubleType);
this.emitLine("break;");
});
}
});
};
const customObjectSerializer: TypeKind[] = ["time", "date", "date-time"];
const serializerCodeForType = (type: Type, fieldName: Sourcelike): Sourcelike => {
switch (type.kind) {
case "date":
return this._dateTimeProvider.convertDateToString(fieldName);
case "time":
return this._dateTimeProvider.convertTimeToString(fieldName);
case "date-time":
return this._dateTimeProvider.convertDateTimeToString(fieldName);
default:
return panic("Requested type doesn't have custom serializer code!");
}
};
const emitSerializeType = (t: Type): void => {
let { fieldName } = this.unionField(u, t, true);
this.emitBlock(["if (obj.", fieldName, " != null)"], () => {
if (customObjectSerializer.some(customSerializerType => t.kind === customSerializerType)) {
this.emitLine("jsonGenerator.writeObject(", serializerCodeForType(t, ["obj.", fieldName]), ");");
} else {
this.emitLine("jsonGenerator.writeObject(obj.", fieldName, ");");
}
this.emitLine("return;");
});
};
const [maybeNull, nonNulls] = removeNullFromUnion(u);
this.ensureBlankLine();
this.emitBlock(["static class Deserializer extends JsonDeserializer<", unionName, ">"], () => {
this.emitLine("@Override");
this.emitBlock(
[
"public ",
unionName,
" deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException"
],
() => {
this.emitLine(unionName, " value = new ", unionName, "();");
this.emitLine("switch (jsonParser.currentToken()) {");
if (maybeNull !== null) emitNullDeserializer();
emitNumberDeserializer();
emitDeserializer(["VALUE_TRUE", "VALUE_FALSE"], "bool");
emitStringDeserializer();
emitDeserializer(["START_ARRAY"], "array");
emitDeserializer(["START_OBJECT"], "class");
emitDeserializer(["START_OBJECT"], "map");
this.indent(() =>
this.emitLine('default: throw new IOException("Cannot deserialize ', unionName, '");')
);
this.emitLine("}");
this.emitLine("return value;");
}
);
});
this.ensureBlankLine();
this.emitBlock(["static class Serializer extends JsonSerializer<", unionName, ">"], () => {
this.emitLine("@Override");
this.emitBlock(
[
"public void serialize(",
unionName,
" obj, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException"
],
() => {
for (const t of nonNulls) {
emitSerializeType(t);
}
if (maybeNull !== null) {
this.emitLine("jsonGenerator.writeNull();");
} else {
this.emitLine('throw new IOException("', unionName, ' must not be null");');
}
}
);
});
}
protected emitEnumSerializationAttributes(_e: EnumType) {
this.emitLine("@JsonValue");
}
protected emitEnumDeserializationAttributes(_e: EnumType) {
this.emitLine("@JsonCreator");
}
protected emitOffsetDateTimeConverterModule(): void {
this.emitLine("SimpleModule module = new SimpleModule();");
if (this._dateTimeProvider.shouldEmitDateTimeConverter) {
this.emitLine(
"module.addDeserializer(",
this._dateTimeProvider.dateTimeType,
".class, new JsonDeserializer<",
this._dateTimeProvider.dateTimeType,
">() {"
);
this.indent(() => {
this.emitLine("@Override");
this.emitBlock(
[
"public ",
this._dateTimeProvider.dateTimeType,
" deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) ",
"throws IOException, JsonProcessingException"
],
() => {
this.emitLine("String value = jsonParser.getText();");
this.emitLine("return ", this._dateTimeProvider.convertStringToDateTime("value"), ";");
}
);
});
this.emitLine("});");
}
if (!this._dateTimeProvider.shouldEmitTimeConverter) {
this.emitLine(
"module.addDeserializer(",
this._dateTimeProvider.timeType,
".class, new JsonDeserializer<",
this._dateTimeProvider.timeType,
">() {"
);
this.indent(() => {
this.emitLine("@Override");
this.emitBlock(
[
"public ",
this._dateTimeProvider.timeType,
" deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) ",
"throws IOException, JsonProcessingException"
],
() => {
this.emitLine("String value = jsonParser.getText();");
this.emitLine("return ", this._dateTimeProvider.convertStringToTime("value"), ";");
}
);
});
this.emitLine("});");
}
if (!this._dateTimeProvider.shouldEmitDateConverter) {
this.emitLine(
"module.addDeserializer(",
this._dateTimeProvider.dateType,
".class, new JsonDeserializer<",
this._dateTimeProvider.dateType,
">() {"
);
this.indent(() => {
this.emitLine("@Override");
this.emitBlock(
[
"public ",
this._dateTimeProvider.dateType,
" deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) ",
"throws IOException, JsonProcessingException"
],
() => {
this.emitLine("String value = jsonParser.getText();");
this.emitLine("return ", this._dateTimeProvider.convertStringToDate("value"), ";");
}
);
});
this.emitLine("});");
}
this.emitLine("mapper.registerModule(module);");
}
protected emitConverterClass(): void {
this.startFile(this._converterClassname);
this.emitCommentLines([
"To use this code, add the following Maven dependency to your project:",
"",
this._options.lombok ? " org.projectlombok : lombok : 1.18.2" : "",
" com.fasterxml.jackson.core : jackson-databind : 2.9.0",
this._options.dateTimeProvider === "java8"
? " com.fasterxml.jackson.datatype : jackson-datatype-jsr310 : 2.9.0"
: "",
"",
"Import this package:",
""
]);
this.emitLine("// import ", this._options.packageName, ".Converter;");
this.emitMultiline(`//
// Then you can deserialize a JSON string with
//`);
this.forEachTopLevel("none", (t, name) => {
this.emitLine(
"// ",
this.javaType(false, t),
" data = Converter.",
this.decoderName(name),
"(jsonString);"
);
});
this.ensureBlankLine();
const imports = [
"java.io.IOException",
"com.fasterxml.jackson.databind.*",
"com.fasterxml.jackson.databind.module.SimpleModule",
"com.fasterxml.jackson.core.JsonParser",
"com.fasterxml.jackson.core.JsonProcessingException",
"java.util.*"
].concat(this._dateTimeProvider.converterImports);
this.emitPackageAndImports(imports);
this.ensureBlankLine();
this.emitBlock(["public class Converter"], () => {
this.emitLine("// Date-time helpers");
this._dateTimeProvider.emitDateTimeConverters();
this.emitLine("// Serialize/deserialize helpers");
this.forEachTopLevel("leading-and-interposing", (topLevelType, topLevelName) => {
const topLevelTypeRendered = this.javaType(false, topLevelType);
this.emitBlock(
[
"public static ",
topLevelTypeRendered,
" ",
this.decoderName(topLevelName),
"(String json) throws IOException"
],
() => {
this.emitLine("return ", this.readerGetterName(topLevelName), "().readValue(json);");
}
);
this.ensureBlankLine();
this.emitBlock(
[
"public static String ",
this.encoderName(topLevelName),
"(",
topLevelTypeRendered,
" obj) throws JsonProcessingException"
],
() => {
this.emitLine("return ", this.writerGetterName(topLevelName), "().writeValueAsString(obj);");
}
);
});
this.forEachTopLevel("leading-and-interposing", (topLevelType, topLevelName) => {
const readerName = this.fieldOrMethodName("reader", topLevelName);
const writerName = this.fieldOrMethodName("writer", topLevelName);
this.emitLine("private static ObjectReader ", readerName, ";");
this.emitLine("private static ObjectWriter ", writerName, ";");
this.ensureBlankLine();
this.emitBlock(
["private static void ", this.methodName("instantiate", "Mapper", topLevelName), "()"],
() => {
const renderedForClass = this.javaTypeWithoutGenerics(false, topLevelType);
this.emitLine("ObjectMapper mapper = new ObjectMapper();");
this.emitLine("mapper.findAndRegisterModules();");
this.emitLine("mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);");
this.emitLine("mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);");
this.emitOffsetDateTimeConverterModule();
this.emitLine(readerName, " = mapper.readerFor(", renderedForClass, ".class);");
this.emitLine(writerName, " = mapper.writerFor(", renderedForClass, ".class);");
}
);
this.ensureBlankLine();
this.emitBlock(["private static ObjectReader ", this.readerGetterName(topLevelName), "()"], () => {
this.emitLine(
"if (",
readerName,
" == null) ",
this.methodName("instantiate", "Mapper", topLevelName),
"();"
);
this.emitLine("return ", readerName, ";");
});
this.ensureBlankLine();
this.emitBlock(["private static ObjectWriter ", this.writerGetterName(topLevelName), "()"], () => {
this.emitLine(
"if (",
writerName,
" == null) ",
this.methodName("instantiate", "Mapper", topLevelName),
"();"
);
this.emitLine("return ", writerName, ";");
});
});
});
this.finishFile();
}
protected emitSourceStructure(): void {
this.emitConverterClass();
super.emitSourceStructure();
}
}

View File

@ -15,7 +15,7 @@ import {
panic,
Sourcelike,
splitIntoWords,
Type,
Type
} from "..";
import { allLowerWordStyle, utf16StringEscape } from "../support/Strings";
import { isES3IdentifierStart } from "./JavaScriptUnicodeMaps";
@ -33,10 +33,10 @@ export const javaScriptPropTypesOptions = {
"Which module system to use",
[
["common-js", false],
["es6", true],
["es6", true]
],
"es6"
),
)
};
export class JavaScriptPropTypesTargetLanguage extends TargetLanguage {
@ -64,7 +64,7 @@ export class JavaScriptPropTypesTargetLanguage extends TargetLanguage {
}
}
const identityNamingFunction = funPrefixNamer("properties", (s) => s);
const identityNamingFunction = funPrefixNamer("properties", s => s);
export class JavaScriptPropTypesRenderer extends ConvenienceRenderer {
constructor(
@ -83,7 +83,7 @@ export class JavaScriptPropTypesRenderer extends ConvenienceRenderer {
legalizeName,
upper ? firstUpperWordStyle : allLowerWordStyle,
firstUpperWordStyle,
upper ? (s) => capitalize(acronyms(s)) : allLowerWordStyle,
upper ? s => capitalize(acronyms(s)) : allLowerWordStyle,
acronyms,
"",
isES3IdentifierStart
@ -91,7 +91,7 @@ export class JavaScriptPropTypesRenderer extends ConvenienceRenderer {
}
protected makeNamedTypeNamer(): Namer {
return funPrefixNamer("types", (s) => this.nameStyle(s, true));
return funPrefixNamer("types", s => this.nameStyle(s, true));
}
protected namerForObjectProperty(): Namer {
@ -103,7 +103,7 @@ export class JavaScriptPropTypesRenderer extends ConvenienceRenderer {
}
protected makeEnumCaseNamer(): Namer {
return funPrefixNamer("enum-cases", (s) => this.nameStyle(s, false));
return funPrefixNamer("enum-cases", s => this.nameStyle(s, false));
}
protected namedTypeToNameForTopLevel(type: Type): Type | undefined {
@ -128,23 +128,23 @@ export class JavaScriptPropTypesRenderer extends ConvenienceRenderer {
const match = matchType<Sourcelike>(
t,
(_anyType) => "PropTypes.any",
(_nullType) => "PropTypes.any",
(_boolType) => "PropTypes.bool",
(_integerType) => "PropTypes.number",
(_doubleType) => "PropTypes.number",
(_stringType) => "PropTypes.string",
(arrayType) => ["PropTypes.arrayOf(", this.typeMapTypeFor(arrayType.items, false), ")"],
(_classType) => panic("Should already be handled."),
(_mapType) => "PropTypes.object",
(_enumType) => panic("Should already be handled."),
(unionType) => {
_anyType => "PropTypes.any",
_nullType => "PropTypes.any",
_boolType => "PropTypes.bool",
_integerType => "PropTypes.number",
_doubleType => "PropTypes.number",
_stringType => "PropTypes.string",
arrayType => ["PropTypes.arrayOf(", this.typeMapTypeFor(arrayType.items, false), ")"],
_classType => panic("Should already be handled."),
_mapType => "PropTypes.object",
_enumType => panic("Should already be handled."),
unionType => {
const children = Array.from(unionType.getChildren()).map((type: Type) =>
this.typeMapTypeFor(type, false)
);
return ["PropTypes.oneOfType([", ...arrayIntercalate(", ", children), "])"];
},
(_transformedStringType) => {
_transformedStringType => {
return "PropTypes.string";
}
);
@ -182,7 +182,7 @@ export class JavaScriptPropTypesRenderer extends ConvenienceRenderer {
"",
"MyComponent.propTypes = {",
" input: MyShape",
"};",
"};"
],
"// "
);
@ -242,7 +242,7 @@ export class JavaScriptPropTypesRenderer extends ConvenienceRenderer {
// pull out all names
const source = mapValue[index];
const names = source.filter((value) => value as Name);
const names = source.filter(value => value as Name);
// must be behind all these names
for (let i = 0; i < names.length; i++) {
@ -263,7 +263,7 @@ export class JavaScriptPropTypesRenderer extends ConvenienceRenderer {
});
// now emit ordered source
order.forEach((i) => this.emitGatheredSource(mapValue[i]));
order.forEach(i => this.emitGatheredSource(mapValue[i]));
// now emit top levels
this.forEachTopLevel("none", (type, name) => {

View File

@ -86,1202 +86,71 @@ export function isES3IdentifierPart(ch: number): boolean {
http://www.unicode.org/Public/3.0-Update/UnicodeData-3.0.0.txt
*/
const unicodeES3IdentifierStart = [
170,
170,
181,
181,
186,
186,
192,
214,
216,
246,
248,
543,
546,
563,
592,
685,
688,
696,
699,
705,
720,
721,
736,
740,
750,
750,
890,
890,
902,
902,
904,
906,
908,
908,
910,
929,
931,
974,
976,
983,
986,
1011,
1024,
1153,
1164,
1220,
1223,
1224,
1227,
1228,
1232,
1269,
1272,
1273,
1329,
1366,
1369,
1369,
1377,
1415,
1488,
1514,
1520,
1522,
1569,
1594,
1600,
1610,
1649,
1747,
1749,
1749,
1765,
1766,
1786,
1788,
1808,
1808,
1810,
1836,
1920,
1957,
2309,
2361,
2365,
2365,
2384,
2384,
2392,
2401,
2437,
2444,
2447,
2448,
2451,
2472,
2474,
2480,
2482,
2482,
2486,
2489,
2524,
2525,
2527,
2529,
2544,
2545,
2565,
2570,
2575,
2576,
2579,
2600,
2602,
2608,
2610,
2611,
2613,
2614,
2616,
2617,
2649,
2652,
2654,
2654,
2674,
2676,
2693,
2699,
2701,
2701,
2703,
2705,
2707,
2728,
2730,
2736,
2738,
2739,
2741,
2745,
2749,
2749,
2768,
2768,
2784,
2784,
2821,
2828,
2831,
2832,
2835,
2856,
2858,
2864,
2866,
2867,
2870,
2873,
2877,
2877,
2908,
2909,
2911,
2913,
2949,
2954,
2958,
2960,
2962,
2965,
2969,
2970,
2972,
2972,
2974,
2975,
2979,
2980,
2984,
2986,
2990,
2997,
2999,
3001,
3077,
3084,
3086,
3088,
3090,
3112,
3114,
3123,
3125,
3129,
3168,
3169,
3205,
3212,
3214,
3216,
3218,
3240,
3242,
3251,
3253,
3257,
3294,
3294,
3296,
3297,
3333,
3340,
3342,
3344,
3346,
3368,
3370,
3385,
3424,
3425,
3461,
3478,
3482,
3505,
3507,
3515,
3517,
3517,
3520,
3526,
3585,
3632,
3634,
3635,
3648,
3654,
3713,
3714,
3716,
3716,
3719,
3720,
3722,
3722,
3725,
3725,
3732,
3735,
3737,
3743,
3745,
3747,
3749,
3749,
3751,
3751,
3754,
3755,
3757,
3760,
3762,
3763,
3773,
3773,
3776,
3780,
3782,
3782,
3804,
3805,
3840,
3840,
3904,
3911,
3913,
3946,
3976,
3979,
4096,
4129,
4131,
4135,
4137,
4138,
4176,
4181,
4256,
4293,
4304,
4342,
4352,
4441,
4447,
4514,
4520,
4601,
4608,
4614,
4616,
4678,
4680,
4680,
4682,
4685,
4688,
4694,
4696,
4696,
4698,
4701,
4704,
4742,
4744,
4744,
4746,
4749,
4752,
4782,
4784,
4784,
4786,
4789,
4792,
4798,
4800,
4800,
4802,
4805,
4808,
4814,
4816,
4822,
4824,
4846,
4848,
4878,
4880,
4880,
4882,
4885,
4888,
4894,
4896,
4934,
4936,
4954,
5024,
5108,
5121,
5740,
5743,
5750,
5761,
5786,
5792,
5866,
6016,
6067,
6176,
6263,
6272,
6312,
7680,
7835,
7840,
7929,
7936,
7957,
7960,
7965,
7968,
8005,
8008,
8013,
8016,
8023,
8025,
8025,
8027,
8027,
8029,
8029,
8031,
8061,
8064,
8116,
8118,
8124,
8126,
8126,
8130,
8132,
8134,
8140,
8144,
8147,
8150,
8155,
8160,
8172,
8178,
8180,
8182,
8188,
8319,
8319,
8450,
8450,
8455,
8455,
8458,
8467,
8469,
8469,
8473,
8477,
8484,
8484,
8486,
8486,
8488,
8488,
8490,
8493,
8495,
8497,
8499,
8505,
8544,
8579,
12293,
12295,
12321,
12329,
12337,
12341,
12344,
12346,
12353,
12436,
12445,
12446,
12449,
12538,
12540,
12542,
12549,
12588,
12593,
12686,
12704,
12727,
13312,
19893,
19968,
40869,
40960,
42124,
44032,
55203,
63744,
64045,
64256,
64262,
64275,
64279,
64285,
64285,
64287,
64296,
64298,
64310,
64312,
64316,
64318,
64318,
64320,
64321,
64323,
64324,
64326,
64433,
64467,
64829,
64848,
64911,
64914,
64967,
65008,
65019,
65136,
65138,
65140,
65140,
65142,
65276,
65313,
65338,
65345,
65370,
65382,
65470,
65474,
65479,
65482,
65487,
65490,
65495,
65498,
65500
170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 543, 546, 563, 592, 685, 688, 696, 699, 705, 720, 721, 736,
740, 750, 750, 890, 890, 902, 902, 904, 906, 908, 908, 910, 929, 931, 974, 976, 983, 986, 1011, 1024, 1153, 1164,
1220, 1223, 1224, 1227, 1228, 1232, 1269, 1272, 1273, 1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522,
1569, 1594, 1600, 1610, 1649, 1747, 1749, 1749, 1765, 1766, 1786, 1788, 1808, 1808, 1810, 1836, 1920, 1957, 2309,
2361, 2365, 2365, 2384, 2384, 2392, 2401, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489,
2524, 2525, 2527, 2529, 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616,
2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2699, 2701, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739,
2741, 2745, 2749, 2749, 2768, 2768, 2784, 2784, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2870,
2873, 2877, 2877, 2908, 2909, 2911, 2913, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975,
2979, 2980, 2984, 2986, 2990, 2997, 2999, 3001, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3168,
3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3294, 3294, 3296, 3297, 3333, 3340, 3342, 3344,
3346, 3368, 3370, 3385, 3424, 3425, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634,
3635, 3648, 3654, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747,
3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805, 3840,
3840, 3904, 3911, 3913, 3946, 3976, 3979, 4096, 4129, 4131, 4135, 4137, 4138, 4176, 4181, 4256, 4293, 4304, 4342,
4352, 4441, 4447, 4514, 4520, 4601, 4608, 4614, 4616, 4678, 4680, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698,
4701, 4704, 4742, 4744, 4744, 4746, 4749, 4752, 4782, 4784, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805,
4808, 4814, 4816, 4822, 4824, 4846, 4848, 4878, 4880, 4880, 4882, 4885, 4888, 4894, 4896, 4934, 4936, 4954, 5024,
5108, 5121, 5740, 5743, 5750, 5761, 5786, 5792, 5866, 6016, 6067, 6176, 6263, 6272, 6312, 7680, 7835, 7840, 7929,
7936, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064,
8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188,
8319, 8319, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490,
8493, 8495, 8497, 8499, 8505, 8544, 8579, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12346, 12353, 12436,
12445, 12446, 12449, 12538, 12540, 12542, 12549, 12588, 12593, 12686, 12704, 12727, 13312, 19893, 19968, 40869,
40960, 42124, 44032, 55203, 63744, 64045, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310,
64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967,
65008, 65019, 65136, 65138, 65140, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479,
65482, 65487, 65490, 65495, 65498, 65500
];
const unicodeES3IdentifierPart = [
170,
170,
181,
181,
186,
186,
192,
214,
216,
246,
248,
543,
546,
563,
592,
685,
688,
696,
699,
705,
720,
721,
736,
740,
750,
750,
768,
846,
864,
866,
890,
890,
902,
902,
904,
906,
908,
908,
910,
929,
931,
974,
976,
983,
986,
1011,
1024,
1153,
1155,
1158,
1164,
1220,
1223,
1224,
1227,
1228,
1232,
1269,
1272,
1273,
1329,
1366,
1369,
1369,
1377,
1415,
1425,
1441,
1443,
1465,
1467,
1469,
1471,
1471,
1473,
1474,
1476,
1476,
1488,
1514,
1520,
1522,
1569,
1594,
1600,
1621,
1632,
1641,
1648,
1747,
1749,
1756,
1759,
1768,
1770,
1773,
1776,
1788,
1808,
1836,
1840,
1866,
1920,
1968,
2305,
2307,
2309,
2361,
2364,
2381,
2384,
2388,
2392,
2403,
2406,
2415,
2433,
2435,
2437,
2444,
2447,
2448,
2451,
2472,
2474,
2480,
2482,
2482,
2486,
2489,
2492,
2492,
2494,
2500,
2503,
2504,
2507,
2509,
2519,
2519,
2524,
2525,
2527,
2531,
2534,
2545,
2562,
2562,
2565,
2570,
2575,
2576,
2579,
2600,
2602,
2608,
2610,
2611,
2613,
2614,
2616,
2617,
2620,
2620,
2622,
2626,
2631,
2632,
2635,
2637,
2649,
2652,
2654,
2654,
2662,
2676,
2689,
2691,
2693,
2699,
2701,
2701,
2703,
2705,
2707,
2728,
2730,
2736,
2738,
2739,
2741,
2745,
2748,
2757,
2759,
2761,
2763,
2765,
2768,
2768,
2784,
2784,
2790,
2799,
2817,
2819,
2821,
2828,
2831,
2832,
2835,
2856,
2858,
2864,
2866,
2867,
2870,
2873,
2876,
2883,
2887,
2888,
2891,
2893,
2902,
2903,
2908,
2909,
2911,
2913,
2918,
2927,
2946,
2947,
2949,
2954,
2958,
2960,
2962,
2965,
2969,
2970,
2972,
2972,
2974,
2975,
2979,
2980,
2984,
2986,
2990,
2997,
2999,
3001,
3006,
3010,
3014,
3016,
3018,
3021,
3031,
3031,
3047,
3055,
3073,
3075,
3077,
3084,
3086,
3088,
3090,
3112,
3114,
3123,
3125,
3129,
3134,
3140,
3142,
3144,
3146,
3149,
3157,
3158,
3168,
3169,
3174,
3183,
3202,
3203,
3205,
3212,
3214,
3216,
3218,
3240,
3242,
3251,
3253,
3257,
3262,
3268,
3270,
3272,
3274,
3277,
3285,
3286,
3294,
3294,
3296,
3297,
3302,
3311,
3330,
3331,
3333,
3340,
3342,
3344,
3346,
3368,
3370,
3385,
3390,
3395,
3398,
3400,
3402,
3405,
3415,
3415,
3424,
3425,
3430,
3439,
3458,
3459,
3461,
3478,
3482,
3505,
3507,
3515,
3517,
3517,
3520,
3526,
3530,
3530,
3535,
3540,
3542,
3542,
3544,
3551,
3570,
3571,
3585,
3642,
3648,
3662,
3664,
3673,
3713,
3714,
3716,
3716,
3719,
3720,
3722,
3722,
3725,
3725,
3732,
3735,
3737,
3743,
3745,
3747,
3749,
3749,
3751,
3751,
3754,
3755,
3757,
3769,
3771,
3773,
3776,
3780,
3782,
3782,
3784,
3789,
3792,
3801,
3804,
3805,
3840,
3840,
3864,
3865,
3872,
3881,
3893,
3893,
3895,
3895,
3897,
3897,
3902,
3911,
3913,
3946,
3953,
3972,
3974,
3979,
3984,
3991,
3993,
4028,
4038,
4038,
4096,
4129,
4131,
4135,
4137,
4138,
4140,
4146,
4150,
4153,
4160,
4169,
4176,
4185,
4256,
4293,
4304,
4342,
4352,
4441,
4447,
4514,
4520,
4601,
4608,
4614,
4616,
4678,
4680,
4680,
4682,
4685,
4688,
4694,
4696,
4696,
4698,
4701,
4704,
4742,
4744,
4744,
4746,
4749,
4752,
4782,
4784,
4784,
4786,
4789,
4792,
4798,
4800,
4800,
4802,
4805,
4808,
4814,
4816,
4822,
4824,
4846,
4848,
4878,
4880,
4880,
4882,
4885,
4888,
4894,
4896,
4934,
4936,
4954,
4969,
4977,
5024,
5108,
5121,
5740,
5743,
5750,
5761,
5786,
5792,
5866,
6016,
6099,
6112,
6121,
6160,
6169,
6176,
6263,
6272,
6313,
7680,
7835,
7840,
7929,
7936,
7957,
7960,
7965,
7968,
8005,
8008,
8013,
8016,
8023,
8025,
8025,
8027,
8027,
8029,
8029,
8031,
8061,
8064,
8116,
8118,
8124,
8126,
8126,
8130,
8132,
8134,
8140,
8144,
8147,
8150,
8155,
8160,
8172,
8178,
8180,
8182,
8188,
8255,
8256,
8319,
8319,
8400,
8412,
8417,
8417,
8450,
8450,
8455,
8455,
8458,
8467,
8469,
8469,
8473,
8477,
8484,
8484,
8486,
8486,
8488,
8488,
8490,
8493,
8495,
8497,
8499,
8505,
8544,
8579,
12293,
12295,
12321,
12335,
12337,
12341,
12344,
12346,
12353,
12436,
12441,
12442,
12445,
12446,
12449,
12542,
12549,
12588,
12593,
12686,
12704,
12727,
13312,
19893,
19968,
40869,
40960,
42124,
44032,
55203,
63744,
64045,
64256,
64262,
64275,
64279,
64285,
64296,
64298,
64310,
64312,
64316,
64318,
64318,
64320,
64321,
64323,
64324,
64326,
64433,
64467,
64829,
64848,
64911,
64914,
64967,
65008,
65019,
65056,
65059,
65075,
65076,
65101,
65103,
65136,
65138,
65140,
65140,
65142,
65276,
65296,
65305,
65313,
65338,
65343,
65343,
65345,
65370,
65381,
65470,
65474,
65479,
65482,
65487,
65490,
65495,
65498,
65500
170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 543, 546, 563, 592, 685, 688, 696, 699, 705, 720, 721, 736,
740, 750, 750, 768, 846, 864, 866, 890, 890, 902, 902, 904, 906, 908, 908, 910, 929, 931, 974, 976, 983, 986, 1011,
1024, 1153, 1155, 1158, 1164, 1220, 1223, 1224, 1227, 1228, 1232, 1269, 1272, 1273, 1329, 1366, 1369, 1369, 1377,
1415, 1425, 1441, 1443, 1465, 1467, 1469, 1471, 1471, 1473, 1474, 1476, 1476, 1488, 1514, 1520, 1522, 1569, 1594,
1600, 1621, 1632, 1641, 1648, 1747, 1749, 1756, 1759, 1768, 1770, 1773, 1776, 1788, 1808, 1836, 1840, 1866, 1920,
1968, 2305, 2307, 2309, 2361, 2364, 2381, 2384, 2388, 2392, 2403, 2406, 2415, 2433, 2435, 2437, 2444, 2447, 2448,
2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2492, 2494, 2500, 2503, 2504, 2507, 2509, 2519, 2519, 2524,
2525, 2527, 2531, 2534, 2545, 2562, 2562, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614,
2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2649, 2652, 2654, 2654, 2662, 2676, 2689, 2691, 2693,
2699, 2701, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765,
2768, 2768, 2784, 2784, 2790, 2799, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2870,
2873, 2876, 2883, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2913, 2918, 2927, 2946, 2947, 2949, 2954,
2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 2997, 2999, 3001, 3006,
3010, 3014, 3016, 3018, 3021, 3031, 3031, 3047, 3055, 3073, 3075, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123,
3125, 3129, 3134, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3168, 3169, 3174, 3183, 3202, 3203, 3205, 3212, 3214,
3216, 3218, 3240, 3242, 3251, 3253, 3257, 3262, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3297,
3302, 3311, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3368, 3370, 3385, 3390, 3395, 3398, 3400, 3402, 3405, 3415,
3415, 3424, 3425, 3430, 3439, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530,
3535, 3540, 3542, 3542, 3544, 3551, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3719,
3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769,
3771, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3805, 3840, 3840, 3864, 3865, 3872, 3881, 3893,
3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3946, 3953, 3972, 3974, 3979, 3984, 3991, 3993, 4028, 4038, 4038,
4096, 4129, 4131, 4135, 4137, 4138, 4140, 4146, 4150, 4153, 4160, 4169, 4176, 4185, 4256, 4293, 4304, 4342, 4352,
4441, 4447, 4514, 4520, 4601, 4608, 4614, 4616, 4678, 4680, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701,
4704, 4742, 4744, 4744, 4746, 4749, 4752, 4782, 4784, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808,
4814, 4816, 4822, 4824, 4846, 4848, 4878, 4880, 4880, 4882, 4885, 4888, 4894, 4896, 4934, 4936, 4954, 4969, 4977,
5024, 5108, 5121, 5740, 5743, 5750, 5761, 5786, 5792, 5866, 6016, 6099, 6112, 6121, 6160, 6169, 6176, 6263, 6272,
6313, 7680, 7835, 7840, 7929, 7936, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027,
8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160,
8172, 8178, 8180, 8182, 8188, 8255, 8256, 8319, 8319, 8400, 8412, 8417, 8417, 8450, 8450, 8455, 8455, 8458, 8467,
8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8497, 8499, 8505, 8544, 8579, 12293,
12295, 12321, 12335, 12337, 12341, 12344, 12346, 12353, 12436, 12441, 12442, 12445, 12446, 12449, 12542, 12549,
12588, 12593, 12686, 12704, 12727, 13312, 19893, 19968, 40869, 40960, 42124, 44032, 55203, 63744, 64045, 64256,
64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326,
64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65056, 65059, 65075, 65076, 65101, 65103, 65136,
65138, 65140, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65381, 65470, 65474,
65479, 65482, 65487, 65490, 65495, 65498, 65500
];

View File

@ -42,14 +42,19 @@ export enum Framework {
None,
Jackson,
Klaxon,
KotlinX,
KotlinX
}
export const kotlinOptions = {
framework: new EnumOption(
"framework",
"Serialization framework",
[["just-types", Framework.None], ["jackson", Framework.Jackson], ["klaxon", Framework.Klaxon], ["kotlinx", Framework.KotlinX]],
[
["just-types", Framework.None],
["jackson", Framework.Jackson],
["klaxon", Framework.Klaxon],
["kotlinx", Framework.KotlinX]
],
"klaxon"
),
acronymStyle: acronymOption(AcronymStyleOptions.Pascal),
@ -156,7 +161,11 @@ function isStartCharacter(codePoint: number): boolean {
const legalizeName = legalizeCharacters(isPartCharacter);
function kotlinNameStyle(isUpper: boolean, original: string, acronymsStyle: (s: string) => string = allUpperWordStyle): string {
function kotlinNameStyle(
isUpper: boolean,
original: string,
acronymsStyle: (s: string) => string = allUpperWordStyle
): string {
const words = splitIntoWords(original);
return combineWords(
words,
@ -401,10 +410,16 @@ export class KotlinRenderer extends ConvenienceRenderer {
{
let table: Sourcelike[][] = [];
this.forEachUnionMember(u, nonNulls, "none", null, (name, t) => {
table.push([["class ", name, "(val value: ", this.kotlinType(t), ")"], [" : ", unionName, "()"]]);
table.push([
["class ", name, "(val value: ", this.kotlinType(t), ")"],
[" : ", unionName, "()"]
]);
});
if (maybeNull !== null) {
table.push([["class ", this.nameForUnionMember(u, maybeNull), "()"], [" : ", unionName, "()"]]);
table.push([
["class ", this.nameForUnionMember(u, maybeNull), "()"],
[" : ", unionName, "()"]
]);
}
this.emitTable(table);
}
@ -1042,7 +1057,11 @@ export class KotlinXRenderer extends KotlinRenderer {
const table: Sourcelike[][] = [];
table.push(["// val ", "json", " = Json { allowStructuredMapKeys = true }"]);
this.forEachTopLevel("none", (_, name) => {
table.push(["// val ", modifySource(camelCase, name), ` = json.parse(${this.sourcelikeToString(name)}.serializer(), jsonString)`]);
table.push([
"// val ",
modifySource(camelCase, name),
` = json.parse(${this.sourcelikeToString(name)}.serializer(), jsonString)`
]);
});
this.emitTable(table);
}
@ -1071,7 +1090,7 @@ export class KotlinXRenderer extends KotlinRenderer {
const escapedName = stringEscape(jsonName);
const namesDiffer = this.sourcelikeToString(propName) !== escapedName;
if (namesDiffer) {
return ["@SerialName(\"", escapedName, "\")"];
return ['@SerialName("', escapedName, '")'];
}
return undefined;
}

View File

@ -975,7 +975,12 @@ export class ObjectiveCRenderer extends ConvenienceRenderer {
}
this.emitMark("Object interfaces");
this.forEachNamedType("leading-and-interposing", (c: ClassType, className: Name) => this.emitClassInterface(c, className), () => null, () => null);
this.forEachNamedType(
"leading-and-interposing",
(c: ClassType, className: Name) => this.emitClassInterface(c, className),
() => null,
() => null
);
this.ensureBlankLine();
this.emitLine("NS_ASSUME_NONNULL_END");
@ -1034,7 +1039,12 @@ export class ObjectiveCRenderer extends ConvenienceRenderer {
this.forEachTopLevel("leading-and-interposing", (t, n) => this.emitTopLevelFunctions(t, n));
}
this.forEachNamedType("leading-and-interposing", (c: ClassType, className: Name) => this.emitClassImplementation(c, className), () => null, () => null);
this.forEachNamedType(
"leading-and-interposing",
(c: ClassType, className: Name) => this.emitClassImplementation(c, className),
() => null,
() => null
);
if (!this._options.justTypes) {
this.ensureBlankLine();

View File

@ -189,7 +189,10 @@ export class PikeRenderer extends ConvenienceRenderer {
this.emitBlock([e.kind, " ", enumName], () => {
let table: Sourcelike[][] = [];
this.forEachEnumCase(e, "none", (name, jsonName) => {
table.push([[name, ' = "', stringEscape(jsonName), '", '], ['// json: "', jsonName, '"']]);
table.push([
[name, ' = "', stringEscape(jsonName), '", '],
['// json: "', jsonName, '"']
]);
});
this.emitTable(table);
});
@ -240,7 +243,11 @@ export class PikeRenderer extends ConvenienceRenderer {
this.forEachClassProperty(c, "none", (name, jsonName, p) => {
const pikeType = this.sourceFor(p.type).source;
table.push([[pikeType, " "], [name, "; "], ['// json: "', jsonName, '"']]);
table.push([
[pikeType, " "],
[name, "; "],
['// json: "', jsonName, '"']
]);
});
this.emitTable(table);
}
@ -275,10 +282,8 @@ export class PikeRenderer extends ConvenienceRenderer {
if (t instanceof PrimitiveType) {
this.emitLine(["return json;"]);
} else if (t instanceof ArrayType) {
if (t.items instanceof PrimitiveType)
this.emitLine(["return json;"]);
else
this.emitLine(["return map(json, ", this.sourceFor(t.items).source, "_from_JSON);"]);
if (t.items instanceof PrimitiveType) this.emitLine(["return json;"]);
else this.emitLine(["return map(json, ", this.sourceFor(t.items).source, "_from_JSON);"]);
} else if (t instanceof MapType) {
const type = this.sourceFor(t.values).source;
this.emitLine(["mapping(string:", type, ") retval = ([]);"]);

View File

@ -1,411 +1,411 @@
import { mapFirst } from "collection-utils";
import { TargetLanguage } from "../TargetLanguage";
import { ConvenienceRenderer, ForbiddenWordsInfo } from "../ConvenienceRenderer";
import {
legalizeCharacters,
splitIntoWords,
isLetterOrUnderscoreOrDigit,
combineWords,
allLowerWordStyle,
firstUpperWordStyle,
intToHex,
utf32ConcatMap,
escapeNonPrintableMapper,
isPrintable,
isAscii,
isLetterOrUnderscore
} from "../support/Strings";
import { Name, Namer, funPrefixNamer } from "../Naming";
import { UnionType, Type, ClassType, EnumType } from "../Type";
import { matchType, nullableFromUnion, removeNullFromUnion } from "../TypeUtils";
import { Sourcelike, maybeAnnotated } from "../Source";
import { anyTypeIssueAnnotation, nullTypeIssueAnnotation } from "../Annotation";
import { BooleanOption, EnumOption, Option, getOptionValues, OptionValues } from "../RendererOptions";
import { defined } from "../support/Support";
import { RenderContext } from "../Renderer";
export enum Density {
Normal,
Dense
}
export enum Visibility {
Private,
Crate,
Public
}
export const rustOptions = {
density: new EnumOption("density", "Density", [
["normal", Density.Normal],
["dense", Density.Dense]
]),
visibility: new EnumOption("visibility", "Field visibility", [
["private", Visibility.Private],
["crate", Visibility.Crate],
["public", Visibility.Public]
]),
deriveDebug: new BooleanOption("derive-debug", "Derive Debug impl", false),
edition2018: new BooleanOption("edition-2018", "Edition 2018", false),
leadingComments: new BooleanOption("leading-comments", "Leading Comments", true)
};
export class RustTargetLanguage extends TargetLanguage {
protected makeRenderer(renderContext: RenderContext, untypedOptionValues: { [name: string]: any }): RustRenderer {
return new RustRenderer(this, renderContext, getOptionValues(rustOptions, untypedOptionValues));
}
constructor() {
super("Rust", ["rust", "rs", "rustlang"], "rs");
}
protected getOptions(): Option<any>[] {
return [
rustOptions.density,
rustOptions.visibility,
rustOptions.deriveDebug,
rustOptions.edition2018,
rustOptions.leadingComments
];
}
}
const keywords = [
// Special reserved identifiers used internally for elided lifetimes,
// unnamed method parameters, crate root module, error recovery etc.
"{{root}}",
"$crate",
// Keywords used in the language.
"as",
"box",
"break",
"const",
"continue",
"crate",
"else",
"enum",
"extern",
"false",
"fn",
"for",
"if",
"impl",
"in",
"let",
"loop",
"match",
"mod",
"move",
"mut",
"pub",
"ref",
"return",
"self",
"Self",
"static",
"struct",
"super",
"trait",
"true",
"type",
"unsafe",
"use",
"where",
"while",
// Keywords reserved for future use.
"abstract",
"alignof",
"become",
"do",
"final",
"macro",
"offsetof",
"override",
"priv",
"proc",
"pure",
"sizeof",
"typeof",
"unsized",
"virtual",
"yield",
// Weak keywords, have special meaning only in specific contexts.
"catch",
"default",
"dyn",
"'static",
"union"
];
const isAsciiLetterOrUnderscoreOrDigit = (codePoint: number): boolean => {
if (!isAscii(codePoint)) {
return false;
}
return isLetterOrUnderscoreOrDigit(codePoint);
};
const isAsciiLetterOrUnderscore = (codePoint: number): boolean => {
if (!isAscii(codePoint)) {
return false;
}
return isLetterOrUnderscore(codePoint);
};
const legalizeName = legalizeCharacters(isAsciiLetterOrUnderscoreOrDigit);
function rustStyle(original: string, isSnakeCase: boolean): string {
const words = splitIntoWords(original);
const wordStyle = isSnakeCase ? allLowerWordStyle : firstUpperWordStyle;
const combined = combineWords(
words,
legalizeName,
wordStyle,
wordStyle,
wordStyle,
wordStyle,
isSnakeCase ? "_" : "",
isAsciiLetterOrUnderscore
);
return combined === "_" ? "_underscore" : combined;
}
const snakeNamingFunction = funPrefixNamer("default", (original: string) => rustStyle(original, true));
const camelNamingFunction = funPrefixNamer("camel", (original: string) => rustStyle(original, false));
const standardUnicodeRustEscape = (codePoint: number): string => {
if (codePoint <= 0xffff) {
return "\\u{" + intToHex(codePoint, 4) + "}";
} else {
return "\\u{" + intToHex(codePoint, 6) + "}";
}
};
const rustStringEscape = utf32ConcatMap(escapeNonPrintableMapper(isPrintable, standardUnicodeRustEscape));
export class RustRenderer extends ConvenienceRenderer {
constructor(
targetLanguage: TargetLanguage,
renderContext: RenderContext,
private readonly _options: OptionValues<typeof rustOptions>
) {
super(targetLanguage, renderContext);
}
protected makeNamedTypeNamer(): Namer {
return camelNamingFunction;
}
protected namerForObjectProperty(): Namer | null {
return snakeNamingFunction;
}
protected makeUnionMemberNamer(): Namer | null {
return camelNamingFunction;
}
protected makeEnumCaseNamer(): Namer | null {
return camelNamingFunction;
}
protected forbiddenNamesForGlobalNamespace(): string[] {
return keywords;
}
protected forbiddenForObjectProperties(_c: ClassType, _className: Name): ForbiddenWordsInfo {
return { names: [], includeGlobalForbidden: true };
}
protected forbiddenForUnionMembers(_u: UnionType, _unionName: Name): ForbiddenWordsInfo {
return { names: [], includeGlobalForbidden: true };
}
protected forbiddenForEnumCases(_e: EnumType, _enumName: Name): ForbiddenWordsInfo {
return { names: [], includeGlobalForbidden: true };
}
protected get commentLineStart(): string {
return "/// ";
}
private nullableRustType(t: Type, withIssues: boolean): Sourcelike {
return ["Option<", this.breakCycle(t, withIssues), ">"];
}
protected isImplicitCycleBreaker(t: Type): boolean {
const kind = t.kind;
return kind === "array" || kind === "map";
}
private rustType(t: Type, withIssues: boolean = false): Sourcelike {
return matchType<Sourcelike>(
t,
_anyType => maybeAnnotated(withIssues, anyTypeIssueAnnotation, "Option<serde_json::Value>"),
_nullType => maybeAnnotated(withIssues, nullTypeIssueAnnotation, "Option<serde_json::Value>"),
_boolType => "bool",
_integerType => "i64",
_doubleType => "f64",
_stringType => "String",
arrayType => ["Vec<", this.rustType(arrayType.items, withIssues), ">"],
classType => this.nameForNamedType(classType),
mapType => ["HashMap<String, ", this.rustType(mapType.values, withIssues), ">"],
enumType => this.nameForNamedType(enumType),
unionType => {
const nullable = nullableFromUnion(unionType);
if (nullable !== null) return this.nullableRustType(nullable, withIssues);
const [hasNull] = removeNullFromUnion(unionType);
const isCycleBreaker = this.isCycleBreakerType(unionType);
const name = isCycleBreaker
? ["Box<", this.nameForNamedType(unionType), ">"]
: this.nameForNamedType(unionType);
return hasNull !== null ? (["Option<", name, ">"] as Sourcelike) : name;
}
);
}
private breakCycle(t: Type, withIssues: boolean): any {
const rustType = this.rustType(t, withIssues);
const isCycleBreaker = this.isCycleBreakerType(t);
return isCycleBreaker ? ["Box<", rustType, ">"] : rustType;
}
private emitRenameAttribute(propName: Name, jsonName: string) {
const escapedName = rustStringEscape(jsonName);
const namesDiffer = this.sourcelikeToString(propName) !== escapedName;
if (namesDiffer || this._options.density === Density.Normal) {
this.emitLine('#[serde(rename = "', escapedName, '")]');
}
}
private get visibility(): string {
if (this._options.visibility === Visibility.Crate) {
return "pub(crate) ";
} else if (this._options.visibility === Visibility.Public) {
return "pub ";
}
return "";
}
protected emitStructDefinition(c: ClassType, className: Name): void {
this.emitDescription(this.descriptionForType(c));
this.emitLine("#[derive(", this._options.deriveDebug ? "Debug, " : "", "Serialize, Deserialize)]");
const blankLines = this._options.density === Density.Dense ? "none" : "interposing";
const structBody = () =>
this.forEachClassProperty(c, blankLines, (name, jsonName, prop) => {
this.emitDescription(this.descriptionForClassProperty(c, jsonName));
this.emitRenameAttribute(name, jsonName);
this.emitLine(this.visibility, name, ": ", this.breakCycle(prop.type, true), ",");
});
this.emitBlock(["pub struct ", className], structBody);
}
protected emitBlock(line: Sourcelike, f: () => void): void {
this.emitLine(line, " {");
this.indent(f);
this.emitLine("}");
}
protected emitUnion(u: UnionType, unionName: Name): void {
const isMaybeWithSingleType = nullableFromUnion(u);
if (isMaybeWithSingleType !== null) {
return;
}
this.emitDescription(this.descriptionForType(u));
this.emitLine("#[derive(", this._options.deriveDebug ? "Debug, " : "", "Serialize, Deserialize)]");
this.emitLine("#[serde(untagged)]");
const [, nonNulls] = removeNullFromUnion(u);
const blankLines = this._options.density === Density.Dense ? "none" : "interposing";
this.emitBlock(["pub enum ", unionName], () =>
this.forEachUnionMember(u, nonNulls, blankLines, null, (fieldName, t) => {
const rustType = this.breakCycle(t, true);
this.emitLine([fieldName, "(", rustType, "),"]);
})
);
}
protected emitEnumDefinition(e: EnumType, enumName: Name): void {
this.emitDescription(this.descriptionForType(e));
this.emitLine("#[derive(", this._options.deriveDebug ? "Debug, " : "", "Serialize, Deserialize)]");
const blankLines = this._options.density === Density.Dense ? "none" : "interposing";
this.emitBlock(["pub enum ", enumName], () =>
this.forEachEnumCase(e, blankLines, (name, jsonName) => {
this.emitRenameAttribute(name, jsonName);
this.emitLine([name, ","]);
})
);
}
protected emitTopLevelAlias(t: Type, name: Name): void {
this.emitLine("pub type ", name, " = ", this.rustType(t), ";");
}
protected emitLeadingComments(): void {
if (this.leadingComments !== undefined) {
this.emitCommentLines(this.leadingComments);
return;
}
const topLevelName = defined(mapFirst(this.topLevels)).getCombinedName();
this.emitMultiline(
`// Example code that deserializes and serializes the model.
// extern crate serde;
// #[macro_use]
// extern crate serde_derive;
// extern crate serde_json;
//
// use generated_module::${topLevelName};
//
// fn main() {
// let json = r#"{"answer": 42}"#;
// let model: ${topLevelName} = serde_json::from_str(&json).unwrap();
// }`
);
}
protected emitSourceStructure(): void {
if (this._options.leadingComments) {
this.emitLeadingComments();
}
this.ensureBlankLine();
if (this._options.edition2018) {
this.emitLine("use serde::{Serialize, Deserialize};");
} else {
this.emitLine("extern crate serde_derive;");
}
if (this.haveMaps) {
this.emitLine("use std::collections::HashMap;");
}
this.forEachTopLevel(
"leading",
(t, name) => this.emitTopLevelAlias(t, name),
t => this.namedTypeToNameForTopLevel(t) === undefined
);
this.forEachObject("leading-and-interposing", (c: ClassType, name: Name) => this.emitStructDefinition(c, name));
this.forEachUnion("leading-and-interposing", (u, name) => this.emitUnion(u, name));
this.forEachEnum("leading-and-interposing", (e, name) => this.emitEnumDefinition(e, name));
}
}
import { mapFirst } from "collection-utils";
import { TargetLanguage } from "../TargetLanguage";
import { ConvenienceRenderer, ForbiddenWordsInfo } from "../ConvenienceRenderer";
import {
legalizeCharacters,
splitIntoWords,
isLetterOrUnderscoreOrDigit,
combineWords,
allLowerWordStyle,
firstUpperWordStyle,
intToHex,
utf32ConcatMap,
escapeNonPrintableMapper,
isPrintable,
isAscii,
isLetterOrUnderscore
} from "../support/Strings";
import { Name, Namer, funPrefixNamer } from "../Naming";
import { UnionType, Type, ClassType, EnumType } from "../Type";
import { matchType, nullableFromUnion, removeNullFromUnion } from "../TypeUtils";
import { Sourcelike, maybeAnnotated } from "../Source";
import { anyTypeIssueAnnotation, nullTypeIssueAnnotation } from "../Annotation";
import { BooleanOption, EnumOption, Option, getOptionValues, OptionValues } from "../RendererOptions";
import { defined } from "../support/Support";
import { RenderContext } from "../Renderer";
export enum Density {
Normal,
Dense
}
export enum Visibility {
Private,
Crate,
Public
}
export const rustOptions = {
density: new EnumOption("density", "Density", [
["normal", Density.Normal],
["dense", Density.Dense]
]),
visibility: new EnumOption("visibility", "Field visibility", [
["private", Visibility.Private],
["crate", Visibility.Crate],
["public", Visibility.Public]
]),
deriveDebug: new BooleanOption("derive-debug", "Derive Debug impl", false),
edition2018: new BooleanOption("edition-2018", "Edition 2018", false),
leadingComments: new BooleanOption("leading-comments", "Leading Comments", true)
};
export class RustTargetLanguage extends TargetLanguage {
protected makeRenderer(renderContext: RenderContext, untypedOptionValues: { [name: string]: any }): RustRenderer {
return new RustRenderer(this, renderContext, getOptionValues(rustOptions, untypedOptionValues));
}
constructor() {
super("Rust", ["rust", "rs", "rustlang"], "rs");
}
protected getOptions(): Option<any>[] {
return [
rustOptions.density,
rustOptions.visibility,
rustOptions.deriveDebug,
rustOptions.edition2018,
rustOptions.leadingComments
];
}
}
const keywords = [
// Special reserved identifiers used internally for elided lifetimes,
// unnamed method parameters, crate root module, error recovery etc.
"{{root}}",
"$crate",
// Keywords used in the language.
"as",
"box",
"break",
"const",
"continue",
"crate",
"else",
"enum",
"extern",
"false",
"fn",
"for",
"if",
"impl",
"in",
"let",
"loop",
"match",
"mod",
"move",
"mut",
"pub",
"ref",
"return",
"self",
"Self",
"static",
"struct",
"super",
"trait",
"true",
"type",
"unsafe",
"use",
"where",
"while",
// Keywords reserved for future use.
"abstract",
"alignof",
"become",
"do",
"final",
"macro",
"offsetof",
"override",
"priv",
"proc",
"pure",
"sizeof",
"typeof",
"unsized",
"virtual",
"yield",
// Weak keywords, have special meaning only in specific contexts.
"catch",
"default",
"dyn",
"'static",
"union"
];
const isAsciiLetterOrUnderscoreOrDigit = (codePoint: number): boolean => {
if (!isAscii(codePoint)) {
return false;
}
return isLetterOrUnderscoreOrDigit(codePoint);
};
const isAsciiLetterOrUnderscore = (codePoint: number): boolean => {
if (!isAscii(codePoint)) {
return false;
}
return isLetterOrUnderscore(codePoint);
};
const legalizeName = legalizeCharacters(isAsciiLetterOrUnderscoreOrDigit);
function rustStyle(original: string, isSnakeCase: boolean): string {
const words = splitIntoWords(original);
const wordStyle = isSnakeCase ? allLowerWordStyle : firstUpperWordStyle;
const combined = combineWords(
words,
legalizeName,
wordStyle,
wordStyle,
wordStyle,
wordStyle,
isSnakeCase ? "_" : "",
isAsciiLetterOrUnderscore
);
return combined === "_" ? "_underscore" : combined;
}
const snakeNamingFunction = funPrefixNamer("default", (original: string) => rustStyle(original, true));
const camelNamingFunction = funPrefixNamer("camel", (original: string) => rustStyle(original, false));
const standardUnicodeRustEscape = (codePoint: number): string => {
if (codePoint <= 0xffff) {
return "\\u{" + intToHex(codePoint, 4) + "}";
} else {
return "\\u{" + intToHex(codePoint, 6) + "}";
}
};
const rustStringEscape = utf32ConcatMap(escapeNonPrintableMapper(isPrintable, standardUnicodeRustEscape));
export class RustRenderer extends ConvenienceRenderer {
constructor(
targetLanguage: TargetLanguage,
renderContext: RenderContext,
private readonly _options: OptionValues<typeof rustOptions>
) {
super(targetLanguage, renderContext);
}
protected makeNamedTypeNamer(): Namer {
return camelNamingFunction;
}
protected namerForObjectProperty(): Namer | null {
return snakeNamingFunction;
}
protected makeUnionMemberNamer(): Namer | null {
return camelNamingFunction;
}
protected makeEnumCaseNamer(): Namer | null {
return camelNamingFunction;
}
protected forbiddenNamesForGlobalNamespace(): string[] {
return keywords;
}
protected forbiddenForObjectProperties(_c: ClassType, _className: Name): ForbiddenWordsInfo {
return { names: [], includeGlobalForbidden: true };
}
protected forbiddenForUnionMembers(_u: UnionType, _unionName: Name): ForbiddenWordsInfo {
return { names: [], includeGlobalForbidden: true };
}
protected forbiddenForEnumCases(_e: EnumType, _enumName: Name): ForbiddenWordsInfo {
return { names: [], includeGlobalForbidden: true };
}
protected get commentLineStart(): string {
return "/// ";
}
private nullableRustType(t: Type, withIssues: boolean): Sourcelike {
return ["Option<", this.breakCycle(t, withIssues), ">"];
}
protected isImplicitCycleBreaker(t: Type): boolean {
const kind = t.kind;
return kind === "array" || kind === "map";
}
private rustType(t: Type, withIssues: boolean = false): Sourcelike {
return matchType<Sourcelike>(
t,
_anyType => maybeAnnotated(withIssues, anyTypeIssueAnnotation, "Option<serde_json::Value>"),
_nullType => maybeAnnotated(withIssues, nullTypeIssueAnnotation, "Option<serde_json::Value>"),
_boolType => "bool",
_integerType => "i64",
_doubleType => "f64",
_stringType => "String",
arrayType => ["Vec<", this.rustType(arrayType.items, withIssues), ">"],
classType => this.nameForNamedType(classType),
mapType => ["HashMap<String, ", this.rustType(mapType.values, withIssues), ">"],
enumType => this.nameForNamedType(enumType),
unionType => {
const nullable = nullableFromUnion(unionType);
if (nullable !== null) return this.nullableRustType(nullable, withIssues);
const [hasNull] = removeNullFromUnion(unionType);
const isCycleBreaker = this.isCycleBreakerType(unionType);
const name = isCycleBreaker
? ["Box<", this.nameForNamedType(unionType), ">"]
: this.nameForNamedType(unionType);
return hasNull !== null ? (["Option<", name, ">"] as Sourcelike) : name;
}
);
}
private breakCycle(t: Type, withIssues: boolean): any {
const rustType = this.rustType(t, withIssues);
const isCycleBreaker = this.isCycleBreakerType(t);
return isCycleBreaker ? ["Box<", rustType, ">"] : rustType;
}
private emitRenameAttribute(propName: Name, jsonName: string) {
const escapedName = rustStringEscape(jsonName);
const namesDiffer = this.sourcelikeToString(propName) !== escapedName;
if (namesDiffer || this._options.density === Density.Normal) {
this.emitLine('#[serde(rename = "', escapedName, '")]');
}
}
private get visibility(): string {
if (this._options.visibility === Visibility.Crate) {
return "pub(crate) ";
} else if (this._options.visibility === Visibility.Public) {
return "pub ";
}
return "";
}
protected emitStructDefinition(c: ClassType, className: Name): void {
this.emitDescription(this.descriptionForType(c));
this.emitLine("#[derive(", this._options.deriveDebug ? "Debug, " : "", "Serialize, Deserialize)]");
const blankLines = this._options.density === Density.Dense ? "none" : "interposing";
const structBody = () =>
this.forEachClassProperty(c, blankLines, (name, jsonName, prop) => {
this.emitDescription(this.descriptionForClassProperty(c, jsonName));
this.emitRenameAttribute(name, jsonName);
this.emitLine(this.visibility, name, ": ", this.breakCycle(prop.type, true), ",");
});
this.emitBlock(["pub struct ", className], structBody);
}
protected emitBlock(line: Sourcelike, f: () => void): void {
this.emitLine(line, " {");
this.indent(f);
this.emitLine("}");
}
protected emitUnion(u: UnionType, unionName: Name): void {
const isMaybeWithSingleType = nullableFromUnion(u);
if (isMaybeWithSingleType !== null) {
return;
}
this.emitDescription(this.descriptionForType(u));
this.emitLine("#[derive(", this._options.deriveDebug ? "Debug, " : "", "Serialize, Deserialize)]");
this.emitLine("#[serde(untagged)]");
const [, nonNulls] = removeNullFromUnion(u);
const blankLines = this._options.density === Density.Dense ? "none" : "interposing";
this.emitBlock(["pub enum ", unionName], () =>
this.forEachUnionMember(u, nonNulls, blankLines, null, (fieldName, t) => {
const rustType = this.breakCycle(t, true);
this.emitLine([fieldName, "(", rustType, "),"]);
})
);
}
protected emitEnumDefinition(e: EnumType, enumName: Name): void {
this.emitDescription(this.descriptionForType(e));
this.emitLine("#[derive(", this._options.deriveDebug ? "Debug, " : "", "Serialize, Deserialize)]");
const blankLines = this._options.density === Density.Dense ? "none" : "interposing";
this.emitBlock(["pub enum ", enumName], () =>
this.forEachEnumCase(e, blankLines, (name, jsonName) => {
this.emitRenameAttribute(name, jsonName);
this.emitLine([name, ","]);
})
);
}
protected emitTopLevelAlias(t: Type, name: Name): void {
this.emitLine("pub type ", name, " = ", this.rustType(t), ";");
}
protected emitLeadingComments(): void {
if (this.leadingComments !== undefined) {
this.emitCommentLines(this.leadingComments);
return;
}
const topLevelName = defined(mapFirst(this.topLevels)).getCombinedName();
this.emitMultiline(
`// Example code that deserializes and serializes the model.
// extern crate serde;
// #[macro_use]
// extern crate serde_derive;
// extern crate serde_json;
//
// use generated_module::${topLevelName};
//
// fn main() {
// let json = r#"{"answer": 42}"#;
// let model: ${topLevelName} = serde_json::from_str(&json).unwrap();
// }`
);
}
protected emitSourceStructure(): void {
if (this._options.leadingComments) {
this.emitLeadingComments();
}
this.ensureBlankLine();
if (this._options.edition2018) {
this.emitLine("use serde::{Serialize, Deserialize};");
} else {
this.emitLine("extern crate serde_derive;");
}
if (this.haveMaps) {
this.emitLine("use std::collections::HashMap;");
}
this.forEachTopLevel(
"leading",
(t, name) => this.emitTopLevelAlias(t, name),
t => this.namedTypeToNameForTopLevel(t) === undefined
);
this.forEachObject("leading-and-interposing", (c: ClassType, name: Name) => this.emitStructDefinition(c, name));
this.forEachUnion("leading-and-interposing", (u, name) => this.emitUnion(u, name));
this.forEachEnum("leading-and-interposing", (e, name) => this.emitEnumDefinition(e, name));
}
}

View File

@ -89,7 +89,6 @@ function quotePropertyName(original: string): string {
}
export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
constructor(
targetLanguage: TargetLanguage,
renderContext: RenderContext,
@ -285,7 +284,7 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer {
if (this._tsFlowOptions.preferUnions) {
let items = "";
e.cases.forEach((item) => {
e.cases.forEach(item => {
if (items === "") {
items += `"${utf16StringEscape(item)}"`;
return;

View File

@ -50,7 +50,8 @@ function attributesForTypes<T extends TypeKind>(types: ReadonlySet<Type>): TypeA
type PropertyMap = Map<string, GenericClassProperty<Set<Type>>>;
class IntersectionAccumulator
implements UnionTypeProvider<ReadonlySet<Type>, [PropertyMap, ReadonlySet<Type> | undefined] | undefined> {
implements UnionTypeProvider<ReadonlySet<Type>, [PropertyMap, ReadonlySet<Type> | undefined] | undefined>
{
private _primitiveTypes: Set<PrimitiveTypeKind> | undefined;
private readonly _primitiveAttributes: TypeAttributeMap<PrimitiveTypeKind> = new Map();
@ -343,7 +344,10 @@ export function resolveIntersections(
const accumulator = new IntersectionAccumulator();
const extraAttributes = makeTypeAttributesInferred(
combineTypeAttributes("intersect", Array.from(members).map(t => accumulator.addType(t)))
combineTypeAttributes(
"intersect",
Array.from(members).map(t => accumulator.addType(t))
)
);
const attributes = combineTypeAttributes("intersect", intersectionAttributes, extraAttributes);
@ -356,9 +360,10 @@ export function resolveIntersections(
}
// FIXME: We need to handle intersections that resolve to the same set of types.
// See for example the intersections-nested.schema example.
const allIntersections = setFilter(graph.allTypesUnordered(), t => t instanceof IntersectionType) as Set<
IntersectionType
>;
const allIntersections = setFilter(
graph.allTypesUnordered(),
t => t instanceof IntersectionType
) as Set<IntersectionType>;
const resolvableIntersections = setFilter(allIntersections, canResolve);
const groups = makeGroupsToFlatten(resolvableIntersections, undefined);
graph = graph.rewrite("resolve intersections", stringTypeMapping, false, groups, debugPrintReconstitution, replace);

View File

@ -1106,7 +1106,7 @@ export enum AcronymStyleOptions {
Lower = "lowerCase"
}
export const acronymOption = function(defaultOption: AcronymStyleOptions) {
export const acronymOption = function (defaultOption: AcronymStyleOptions) {
return new EnumOption(
"acronym-style",
"Acronym naming style",

View File

@ -12,9 +12,10 @@ export type NamingStyle =
const unicode = require("unicode-properties");
function computeAsciiMap(
mapper: (codePoint: number) => string
): { charStringMap: string[]; charNoEscapeMap: number[] } {
function computeAsciiMap(mapper: (codePoint: number) => string): {
charStringMap: string[];
charNoEscapeMap: number[];
} {
const charStringMap: string[] = [];
const charNoEscapeMap: number[] = [];

View File

@ -115,7 +115,10 @@ function removeNull(builder: TypeBuilder, tref: TypeRef): TypeRef {
const first = iterableFirst(nonNulls);
if (first) {
if (nonNulls.size === 1) return first.typeRef;
return builder.getUnionType(t.getAttributes(), setMap(nonNulls, nn => nn.typeRef));
return builder.getUnionType(
t.getAttributes(),
setMap(nonNulls, nn => nn.typeRef)
);
}
return panic("Trying to remove null results in empty union.");
}

File diff suppressed because one or more lines are too long

View File

@ -5,46 +5,46 @@ import { WorkItem } from "./test";
import { allFixtures, Fixture } from "./fixtures";
function getChangedFiles(base: string, commit: string): string[] {
let diff = exec(`git fetch -v origin ${base} && git diff --name-only origin/${base}..${commit}`).stdout;
return diff.trim().split("\n");
let diff = exec(`git fetch -v origin ${base} && git diff --name-only origin/${base}..${commit}`).stdout;
return diff.trim().split("\n");
}
export function affectedFixtures(changedFiles: string[] | undefined = undefined): Fixture[] {
if (changedFiles === undefined) {
const { GITHUB_BASE_REF: base, GITHUB_SHA: commit } = process.env;
return commit === undefined ? allFixtures : affectedFixtures(getChangedFiles(base || "master", commit));
}
if (changedFiles === undefined) {
const { GITHUB_BASE_REF: base, GITHUB_SHA: commit } = process.env;
return commit === undefined ? allFixtures : affectedFixtures(getChangedFiles(base || "master", commit));
}
// We can ignore changes in Markdown files
changedFiles = _.reject(changedFiles, (file) => _.endsWith(file, ".md"));
// We can ignore changes in Markdown files
changedFiles = _.reject(changedFiles, file => _.endsWith(file, ".md"));
// All fixtures are dirty if any changed file is not included as a sourceFile of some fixture.
const fileDependencies = _.flatMap(allFixtures, (f) => f.language.sourceFiles || []);
const allFixturesDirty = _.some(changedFiles, (f) => !_.includes(fileDependencies, f));
// All fixtures are dirty if any changed file is not included as a sourceFile of some fixture.
const fileDependencies = _.flatMap(allFixtures, f => f.language.sourceFiles || []);
const allFixturesDirty = _.some(changedFiles, f => !_.includes(fileDependencies, f));
if (allFixturesDirty) return allFixtures;
if (allFixturesDirty) return allFixtures;
const dirtyFixtures = allFixtures.filter(
(fixture) =>
// Fixtures that don't specify dependencies are always dirty
fixture.language.sourceFiles === undefined ||
// Fixtures that have a changed file are dirty
_.some(changedFiles, (f) => _.includes(fixture.language.sourceFiles, f))
);
const dirtyFixtures = allFixtures.filter(
fixture =>
// Fixtures that don't specify dependencies are always dirty
fixture.language.sourceFiles === undefined ||
// Fixtures that have a changed file are dirty
_.some(changedFiles, f => _.includes(fixture.language.sourceFiles, f))
);
return dirtyFixtures;
return dirtyFixtures;
}
export function divideParallelJobs(workItems: WorkItem[]): WorkItem[] {
const { BUILDKITE_PARALLEL_JOB: pjob, BUILDKITE_PARALLEL_JOB_COUNT: pcount } = process.env;
const { BUILDKITE_PARALLEL_JOB: pjob, BUILDKITE_PARALLEL_JOB_COUNT: pcount } = process.env;
if (pjob === undefined || pcount === undefined) return workItems;
if (pjob === undefined || pcount === undefined) return workItems;
try {
const segment = Math.ceil(workItems.length / parseFloat(pcount));
const start = parseInt(pjob, 10) * segment;
return workItems.slice(start, start + segment);
} catch {
return workItems;
}
try {
const segment = Math.ceil(workItems.length / parseFloat(pcount));
const start = parseInt(pjob, 10) * segment;
return workItems.slice(start, start + segment);
} catch {
return workItems;
}
}

View File

@ -7,20 +7,20 @@ import * as shell from "shelljs";
const Ajv = require("ajv");
import {
compareJsonFileToJson,
debug,
exec,
execAsync,
failWith,
inDir,
quicktype,
quicktypeForLanguage,
Sample,
samplesFromSources,
testsInDir,
ComparisonArgs,
mkdirs,
callAndExpectFailure,
compareJsonFileToJson,
debug,
exec,
execAsync,
failWith,
inDir,
quicktype,
quicktypeForLanguage,
Sample,
samplesFromSources,
testsInDir,
ComparisonArgs,
mkdirs,
callAndExpectFailure
} from "./utils";
import * as languages from "./languages";
import { RendererOptions } from "../dist/quicktype-core/Run";
@ -42,320 +42,316 @@ const MAX_TEST_RUNTIME_MS = 30 * 60 * 1000;
const testsWithStringifiedIntegers = ["nst-test-suite.json", "kitchen-sink.json"];
function allowStringifiedIntegers(language: languages.Language, test: string): boolean {
if (language.features.indexOf("integer-string") < 0) return false;
return testsWithStringifiedIntegers.indexOf(path.basename(test)) >= 0;
if (language.features.indexOf("integer-string") < 0) return false;
return testsWithStringifiedIntegers.indexOf(path.basename(test)) >= 0;
}
function pathWithoutExtension(fullPath: string, extension: string): string {
return path.join(path.dirname(fullPath), path.basename(fullPath, extension));
return path.join(path.dirname(fullPath), path.basename(fullPath, extension));
}
function additionalTestFiles(base: string, extension: string, features: string[] = []): string[] {
const additionalFiles: string[] = [];
function tryAdd(filename: string): boolean {
if (!fs.existsSync(filename)) return false;
additionalFiles.push(filename);
return true;
}
let fn = `${base}.${extension}`;
tryAdd(fn);
let i = 1;
let found: boolean;
do {
found = false;
fn = `${base}.${i.toString()}.${extension}`;
found = tryAdd(fn) || found;
for (const feature of features) {
found = tryAdd(`${base}.${i.toString()}.fail.${feature}.${extension}`) || found;
const additionalFiles: string[] = [];
function tryAdd(filename: string): boolean {
if (!fs.existsSync(filename)) return false;
additionalFiles.push(filename);
return true;
}
found = tryAdd(`${base}.${i.toString()}.fail.${extension}`) || found;
i++;
} while (found);
return additionalFiles;
let fn = `${base}.${extension}`;
tryAdd(fn);
let i = 1;
let found: boolean;
do {
found = false;
fn = `${base}.${i.toString()}.${extension}`;
found = tryAdd(fn) || found;
for (const feature of features) {
found = tryAdd(`${base}.${i.toString()}.fail.${feature}.${extension}`) || found;
}
found = tryAdd(`${base}.${i.toString()}.fail.${extension}`) || found;
i++;
} while (found);
return additionalFiles;
}
function runEnvForLanguage(additionalRendererOptions: RendererOptions): NodeJS.ProcessEnv {
const newEnv = Object.assign({}, process.env);
for (const o of Object.getOwnPropertyNames(additionalRendererOptions)) {
newEnv["QUICKTYPE_" + o.toUpperCase().replace("-", "_")] = additionalRendererOptions[o];
}
return newEnv;
const newEnv = Object.assign({}, process.env);
for (const o of Object.getOwnPropertyNames(additionalRendererOptions)) {
newEnv["QUICKTYPE_" + o.toUpperCase().replace("-", "_")] = additionalRendererOptions[o];
}
return newEnv;
}
function comparisonArgs(
language: languages.Language,
inputFilename: string,
expectedFilename: string,
additionalRendererOptions: RendererOptions
language: languages.Language,
inputFilename: string,
expectedFilename: string,
additionalRendererOptions: RendererOptions
): ComparisonArgs {
return {
expectedFile: expectedFilename,
given: {
command: defined(language.runCommand)(inputFilename),
env: runEnvForLanguage(additionalRendererOptions),
},
strict: false,
allowMissingNull: language.allowMissingNull,
allowStringifiedIntegers: allowStringifiedIntegers(language, expectedFilename),
};
return {
expectedFile: expectedFilename,
given: {
command: defined(language.runCommand)(inputFilename),
env: runEnvForLanguage(additionalRendererOptions)
},
strict: false,
allowMissingNull: language.allowMissingNull,
allowStringifiedIntegers: allowStringifiedIntegers(language, expectedFilename)
};
}
const timeMap = new Map<string, number>();
function timeStart(message: string): void {
timeMap.set(message, Date.now());
timeMap.set(message, Date.now());
}
function timeEnd(message: string, suffix: string): void {
const start = timeMap.get(message);
const fullMessage = message + suffix;
if (start === undefined) {
console.log(fullMessage + ": " + chalk.red("UNKNOWN TIMING"));
return;
}
const diff = Date.now() - start;
console.log(fullMessage + `: ${diff} ms`);
const start = timeMap.get(message);
const fullMessage = message + suffix;
if (start === undefined) {
console.log(fullMessage + ": " + chalk.red("UNKNOWN TIMING"));
return;
}
const diff = Date.now() - start;
console.log(fullMessage + `: ${diff} ms`);
}
export abstract class Fixture {
abstract name: string;
abstract name: string;
constructor(public language: languages.Language) {}
constructor(public language: languages.Language) {}
runForName(name: string): boolean {
return this.name === name;
}
async setup(): Promise<void> {
return;
}
abstract getSamples(sources: string[]): { priority: Sample[]; others: Sample[] };
abstract runWithSample(sample: Sample, index: number, total: number): Promise<void>;
getRunDirectory(): string {
return `test/runs/${this.name}-${randomBytes(3).toString("hex")}`;
}
runMessageStart(sample: Sample, index: number, total: number, cwd: string, shouldSkip: boolean): string {
const rendererOptions = _.map(sample.additionalRendererOptions, (v, k) => `${k}: ${v}`).join(", ");
const messageParts = [
`*`,
chalk.dim(`[${index + 1}/${total}]`),
chalk.magenta(this.name) + chalk.dim(`(${rendererOptions})`),
path.join(cwd, chalk.cyan(path.basename(sample.path))),
];
if (shouldSkip) {
messageParts.push(chalk.red("SKIP"));
runForName(name: string): boolean {
return this.name === name;
}
const message = messageParts.join(" ");
timeStart(message);
return message;
}
runMessageEnd(message: string, numFiles: number) {
const numFilesString = ` (${numFiles} files)`;
const suffix =
numFiles <= 0 ? chalk.red(numFilesString) : numFiles > 1 ? chalk.green(numFilesString) : "";
timeEnd(message, suffix);
}
async setup(): Promise<void> {
return;
}
abstract getSamples(sources: string[]): { priority: Sample[]; others: Sample[] };
abstract runWithSample(sample: Sample, index: number, total: number): Promise<void>;
getRunDirectory(): string {
return `test/runs/${this.name}-${randomBytes(3).toString("hex")}`;
}
runMessageStart(sample: Sample, index: number, total: number, cwd: string, shouldSkip: boolean): string {
const rendererOptions = _.map(sample.additionalRendererOptions, (v, k) => `${k}: ${v}`).join(", ");
const messageParts = [
`*`,
chalk.dim(`[${index + 1}/${total}]`),
chalk.magenta(this.name) + chalk.dim(`(${rendererOptions})`),
path.join(cwd, chalk.cyan(path.basename(sample.path)))
];
if (shouldSkip) {
messageParts.push(chalk.red("SKIP"));
}
const message = messageParts.join(" ");
timeStart(message);
return message;
}
runMessageEnd(message: string, numFiles: number) {
const numFilesString = ` (${numFiles} files)`;
const suffix = numFiles <= 0 ? chalk.red(numFilesString) : numFiles > 1 ? chalk.green(numFilesString) : "";
timeEnd(message, suffix);
}
}
abstract class LanguageFixture extends Fixture {
constructor(language: languages.Language) {
super(language);
}
async setup() {
const setupCommand = this.language.setupCommand;
if (!setupCommand || ONLY_OUTPUT) {
return;
constructor(language: languages.Language) {
super(language);
}
console.error(`* Setting up`, chalk.magenta(this.name), `fixture`);
async setup() {
const setupCommand = this.language.setupCommand;
if (!setupCommand || ONLY_OUTPUT) {
return;
}
await inDir(this.language.base, async () => {
await execAsync(setupCommand);
});
}
console.error(`* Setting up`, chalk.magenta(this.name), `fixture`);
abstract shouldSkipTest(sample: Sample): boolean;
abstract async runQuicktype(filename: string, additionalRendererOptions: RendererOptions): Promise<void>;
abstract async test(
filename: string,
additionalRendererOptions: RendererOptions,
additionalFiles: string[]
): Promise<number>;
additionalFiles(_sample: Sample): string[] {
return [];
}
async runWithSample(sample: Sample, index: number, total: number) {
const cwd = this.getRunDirectory();
const sampleFile = path.resolve(sample.path);
const shouldSkip = this.shouldSkipTest(sample);
const additionalFiles = this.additionalFiles(sample).map((p) => path.resolve(p));
const message = this.runMessageStart(sample, index, total, cwd, shouldSkip);
if (shouldSkip) {
return;
await inDir(this.language.base, async () => {
await execAsync(setupCommand);
});
}
shell.cp("-R", this.language.base, cwd);
abstract shouldSkipTest(sample: Sample): boolean;
abstract async runQuicktype(filename: string, additionalRendererOptions: RendererOptions): Promise<void>;
abstract async test(
filename: string,
additionalRendererOptions: RendererOptions,
additionalFiles: string[]
): Promise<number>;
if (this.language.copyInput) {
shell.cp(sampleFile, cwd);
additionalFiles(_sample: Sample): string[] {
return [];
}
let numFiles = -1;
await inDir(cwd, async () => {
await this.runQuicktype(sampleFile, sample.additionalRendererOptions);
async runWithSample(sample: Sample, index: number, total: number) {
const cwd = this.getRunDirectory();
const sampleFile = path.resolve(sample.path);
const shouldSkip = this.shouldSkipTest(sample);
const additionalFiles = this.additionalFiles(sample).map(p => path.resolve(p));
if (ONLY_OUTPUT) {
return;
}
const message = this.runMessageStart(sample, index, total, cwd, shouldSkip);
try {
numFiles = await timeout(
this.test(sampleFile, sample.additionalRendererOptions, additionalFiles),
MAX_TEST_RUNTIME_MS
);
} catch (e) {
failWith("Fixture threw an exception", { error: e, sample });
}
});
if (shouldSkip) {
return;
}
// FIXME: This is an ugly hack to exclude Java, which has multiple
// output files. We have to support that eventually.
if (sample.saveOutput && OUTPUT_DIR !== undefined && this.language.output.indexOf("/") < 0) {
const outputDir = path.join(
OUTPUT_DIR,
this.language.name,
path.dirname(sample.path),
path.basename(sample.path, path.extname(sample.path))
);
mkdirs(outputDir);
shell.cp(path.join(cwd, this.language.output), outputDir);
shell.cp("-R", this.language.base, cwd);
if (this.language.copyInput) {
shell.cp(sampleFile, cwd);
}
let numFiles = -1;
await inDir(cwd, async () => {
await this.runQuicktype(sampleFile, sample.additionalRendererOptions);
if (ONLY_OUTPUT) {
return;
}
try {
numFiles = await timeout(
this.test(sampleFile, sample.additionalRendererOptions, additionalFiles),
MAX_TEST_RUNTIME_MS
);
} catch (e) {
failWith("Fixture threw an exception", { error: e, sample });
}
});
// FIXME: This is an ugly hack to exclude Java, which has multiple
// output files. We have to support that eventually.
if (sample.saveOutput && OUTPUT_DIR !== undefined && this.language.output.indexOf("/") < 0) {
const outputDir = path.join(
OUTPUT_DIR,
this.language.name,
path.dirname(sample.path),
path.basename(sample.path, path.extname(sample.path))
);
mkdirs(outputDir);
shell.cp(path.join(cwd, this.language.output), outputDir);
}
this.runMessageEnd(message, numFiles);
}
this.runMessageEnd(message, numFiles);
}
}
class JSONFixture extends LanguageFixture {
constructor(language: languages.Language, public name: string = language.name) {
super(language);
}
runForName(name: string): boolean {
return this.name === name || name === "json";
}
async runQuicktype(sample: string, additionalRendererOptions: RendererOptions): Promise<void> {
// FIXME: add options
await quicktypeForLanguage(this.language, sample, "json", true, additionalRendererOptions);
}
async test(
filename: string,
additionalRendererOptions: RendererOptions,
_additionalFiles: string[]
): Promise<number> {
if (this.language.compileCommand) {
await execAsync(this.language.compileCommand);
}
if (this.language.runCommand === undefined) return 0;
compareJsonFileToJson(comparisonArgs(this.language, filename, filename, additionalRendererOptions));
if (
this.language.diffViaSchema &&
!_.includes(this.language.skipDiffViaSchema, path.basename(filename))
) {
debug("* Diffing with code generated via JSON Schema");
// Make a schema
await quicktype({
src: [filename],
lang: "schema",
out: "schema.json",
topLevel: this.language.topLevel,
rendererOptions: {},
});
// Quicktype from the schema and compare to expected code
shell.mv(this.language.output, `${this.language.output}.expected`);
await quicktypeForLanguage(this.language, "schema.json", "schema", true, additionalRendererOptions);
// Compare fixture.output to fixture.output.expected
exec(`diff -Naur ${this.language.output}.expected ${this.language.output} > /dev/null 2>&1`, undefined);
constructor(language: languages.Language, public name: string = language.name) {
super(language);
}
return 1;
}
shouldSkipTest(sample: Sample): boolean {
if (fs.statSync(sample.path).size > 32 * 1024 * 1024) {
return true;
}
return _.includes(this.language.skipJSON, path.basename(sample.path));
}
getSamples(sources: string[]): { priority: Sample[]; others: Sample[] } {
// FIXME: this should only run once
const prioritySamples = _.concat(
testsInDir("test/inputs/json/priority", "json"),
testsInDir("test/inputs/json/samples", "json")
);
const skipMiscJSON = process.env.QUICKTEST !== undefined || this.language.skipMiscJSON;
const miscSamples = skipMiscJSON ? [] : testsInDir("test/inputs/json/misc", "json");
let { priority, others } = samplesFromSources(sources, prioritySamples, miscSamples, "json");
const combinationInputs = _.map([1, 2, 3, 4], (n) =>
_.find(prioritySamples, (p) => p.endsWith(`/priority/combinations${n}.json`))
);
if (combinationInputs.some((p) => p === undefined)) {
return failWith("priority/combinations[1234].json samples not found", prioritySamples);
}
if (sources.length === 0 && !ONLY_OUTPUT) {
const quickTestSamples = _.chain(this.language.quickTestRendererOptions)
.flatMap((qt) => {
if (Array.isArray(qt)) {
const [filename, ro] = qt;
const input = _.find(([] as string[]).concat(prioritySamples, miscSamples), (p) =>
p.endsWith(`/${filename}`)
);
if (input === undefined) {
return failWith(`quick-test sample ${filename} not found`, qt);
}
return [
{
path: input,
additionalRendererOptions: ro,
saveOutput: false,
},
];
} else {
return _.map(combinationInputs, (p) => ({
path: defined(p),
additionalRendererOptions: qt,
saveOutput: false,
}));
}
})
.value();
priority = quickTestSamples.concat(priority);
runForName(name: string): boolean {
return this.name === name || name === "json";
}
return { priority, others };
}
async runQuicktype(sample: string, additionalRendererOptions: RendererOptions): Promise<void> {
// FIXME: add options
await quicktypeForLanguage(this.language, sample, "json", true, additionalRendererOptions);
}
async test(
filename: string,
additionalRendererOptions: RendererOptions,
_additionalFiles: string[]
): Promise<number> {
if (this.language.compileCommand) {
await execAsync(this.language.compileCommand);
}
if (this.language.runCommand === undefined) return 0;
compareJsonFileToJson(comparisonArgs(this.language, filename, filename, additionalRendererOptions));
if (this.language.diffViaSchema && !_.includes(this.language.skipDiffViaSchema, path.basename(filename))) {
debug("* Diffing with code generated via JSON Schema");
// Make a schema
await quicktype({
src: [filename],
lang: "schema",
out: "schema.json",
topLevel: this.language.topLevel,
rendererOptions: {}
});
// Quicktype from the schema and compare to expected code
shell.mv(this.language.output, `${this.language.output}.expected`);
await quicktypeForLanguage(this.language, "schema.json", "schema", true, additionalRendererOptions);
// Compare fixture.output to fixture.output.expected
exec(`diff -Naur ${this.language.output}.expected ${this.language.output} > /dev/null 2>&1`, undefined);
}
return 1;
}
shouldSkipTest(sample: Sample): boolean {
if (fs.statSync(sample.path).size > 32 * 1024 * 1024) {
return true;
}
return _.includes(this.language.skipJSON, path.basename(sample.path));
}
getSamples(sources: string[]): { priority: Sample[]; others: Sample[] } {
// FIXME: this should only run once
const prioritySamples = _.concat(
testsInDir("test/inputs/json/priority", "json"),
testsInDir("test/inputs/json/samples", "json")
);
const skipMiscJSON = process.env.QUICKTEST !== undefined || this.language.skipMiscJSON;
const miscSamples = skipMiscJSON ? [] : testsInDir("test/inputs/json/misc", "json");
let { priority, others } = samplesFromSources(sources, prioritySamples, miscSamples, "json");
const combinationInputs = _.map([1, 2, 3, 4], n =>
_.find(prioritySamples, p => p.endsWith(`/priority/combinations${n}.json`))
);
if (combinationInputs.some(p => p === undefined)) {
return failWith("priority/combinations[1234].json samples not found", prioritySamples);
}
if (sources.length === 0 && !ONLY_OUTPUT) {
const quickTestSamples = _.chain(this.language.quickTestRendererOptions)
.flatMap(qt => {
if (Array.isArray(qt)) {
const [filename, ro] = qt;
const input = _.find(([] as string[]).concat(prioritySamples, miscSamples), p =>
p.endsWith(`/${filename}`)
);
if (input === undefined) {
return failWith(`quick-test sample ${filename} not found`, qt);
}
return [
{
path: input,
additionalRendererOptions: ro,
saveOutput: false
}
];
} else {
return _.map(combinationInputs, p => ({
path: defined(p),
additionalRendererOptions: qt,
saveOutput: false
}));
}
})
.value();
priority = quickTestSamples.concat(priority);
}
return { priority, others };
}
}
// This fixture tests generating code for language X from JSON,
@ -363,66 +359,66 @@ class JSONFixture extends LanguageFixture {
// that the resulting code for Y accepts the JSON by running it
// on the original JSON.
class JSONToXToYFixture extends JSONFixture {
private readonly runLanguage: languages.Language;
private readonly runLanguage: languages.Language;
constructor(
private readonly _fixturePrefix: string,
languageXName: string,
languageXOutputFilename: string,
rendererOptions: RendererOptions,
skipJSON: string[],
language: languages.Language
) {
super({
name: languageXName,
base: language.base,
setupCommand: language.setupCommand,
runCommand: mustNotHappen,
diffViaSchema: false,
skipDiffViaSchema: [],
allowMissingNull: language.allowMissingNull,
features: language.features,
output: languageXOutputFilename,
topLevel: "TopLevel",
skipJSON,
skipMiscJSON: false,
skipSchema: [],
rendererOptions,
quickTestRendererOptions: [],
sourceFiles: language.sourceFiles,
});
this.runLanguage = language;
this.name = `${this._fixturePrefix}-${language.name}`;
}
constructor(
private readonly _fixturePrefix: string,
languageXName: string,
languageXOutputFilename: string,
rendererOptions: RendererOptions,
skipJSON: string[],
language: languages.Language
) {
super({
name: languageXName,
base: language.base,
setupCommand: language.setupCommand,
runCommand: mustNotHappen,
diffViaSchema: false,
skipDiffViaSchema: [],
allowMissingNull: language.allowMissingNull,
features: language.features,
output: languageXOutputFilename,
topLevel: "TopLevel",
skipJSON,
skipMiscJSON: false,
skipSchema: [],
rendererOptions,
quickTestRendererOptions: [],
sourceFiles: language.sourceFiles
});
this.runLanguage = language;
this.name = `${this._fixturePrefix}-${language.name}`;
}
runForName(name: string): boolean {
return this.name === name || name === this._fixturePrefix;
}
runForName(name: string): boolean {
return this.name === name || name === this._fixturePrefix;
}
async test(
filename: string,
additionalRendererOptions: RendererOptions,
_additionalFiles: string[]
): Promise<number> {
// Generate code for Y from X
await quicktypeForLanguage(
this.runLanguage,
this.language.output,
this.language.name,
false,
additionalRendererOptions
);
async test(
filename: string,
additionalRendererOptions: RendererOptions,
_additionalFiles: string[]
): Promise<number> {
// Generate code for Y from X
await quicktypeForLanguage(
this.runLanguage,
this.language.output,
this.language.name,
false,
additionalRendererOptions
);
// Parse the sample with the code generated from its schema, and compare to the sample
compareJsonFileToJson(comparisonArgs(this.runLanguage, filename, filename, additionalRendererOptions));
// Parse the sample with the code generated from its schema, and compare to the sample
compareJsonFileToJson(comparisonArgs(this.runLanguage, filename, filename, additionalRendererOptions));
return 1;
}
return 1;
}
shouldSkipTest(sample: Sample): boolean {
if (super.shouldSkipTest(sample)) return true;
return _.includes(this.runLanguage.skipJSON, path.basename(sample.path));
}
shouldSkipTest(sample: Sample): boolean {
if (super.shouldSkipTest(sample)) return true;
return _.includes(this.runLanguage.skipJSON, path.basename(sample.path));
}
}
const dateTimeRecognizer = new DefaultDateTimeRecognizer();
@ -432,420 +428,420 @@ const dateTimeRecognizer = new DefaultDateTimeRecognizer();
// the original JSON. Also generating a Schema from the Schema
// and testing that it's the same as the original Schema.
class JSONSchemaJSONFixture extends JSONToXToYFixture {
constructor(language: languages.Language) {
const skipJSON = [
"blns-object.json", // AJV refuses to even "compile" the schema we generate
"31189.json", // same here
"437e7.json", // uri/string confusion
"ed095.json", // same here on Travis
];
super("schema-json", "schema", "schema.json", {}, skipJSON, language);
}
async test(
filename: string,
additionalRendererOptions: RendererOptions,
additionalFiles: string[]
): Promise<number> {
let input = JSON.parse(fs.readFileSync(filename, "utf8"));
let schema = JSON.parse(fs.readFileSync(this.language.output, "utf8"));
let ajv = new Ajv({ format: "full", unknownFormats: ["integer", "boolean"] });
// Make Ajv's date-time compatible with what we recognize. All non-standard
// JSON formats that we use for transformed type kinds must be registered here
// with a validation function.
// FIXME: Unify this with what's in StringTypes.ts.
ajv.addFormat("date-time", (s: string) => dateTimeRecognizer.isDateTime(s));
let valid = ajv.validate(schema, input);
if (!valid) {
failWith("Generated schema does not validate input JSON.", {
filename,
});
constructor(language: languages.Language) {
const skipJSON = [
"blns-object.json", // AJV refuses to even "compile" the schema we generate
"31189.json", // same here
"437e7.json", // uri/string confusion
"ed095.json" // same here on Travis
];
super("schema-json", "schema", "schema.json", {}, skipJSON, language);
}
await super.test(filename, additionalRendererOptions, additionalFiles);
async test(
filename: string,
additionalRendererOptions: RendererOptions,
additionalFiles: string[]
): Promise<number> {
let input = JSON.parse(fs.readFileSync(filename, "utf8"));
let schema = JSON.parse(fs.readFileSync(this.language.output, "utf8"));
// Generate a schema from the schema, making sure the schemas are the same
// FIXME: We could move this to the superclass and test it for all JSON->X->Y
let schemaSchema = "schema-from-schema.json";
await quicktype({
src: [this.language.output],
srcLang: this.language.name,
lang: this.language.name,
topLevel: this.language.topLevel,
out: schemaSchema,
rendererOptions: {},
});
compareJsonFileToJson({
expectedFile: this.language.output,
given: { file: schemaSchema },
strict: true,
});
let ajv = new Ajv({ format: "full", unknownFormats: ["integer", "boolean"] });
// Make Ajv's date-time compatible with what we recognize. All non-standard
// JSON formats that we use for transformed type kinds must be registered here
// with a validation function.
// FIXME: Unify this with what's in StringTypes.ts.
ajv.addFormat("date-time", (s: string) => dateTimeRecognizer.isDateTime(s));
let valid = ajv.validate(schema, input);
if (!valid) {
failWith("Generated schema does not validate input JSON.", {
filename
});
}
return 1;
}
await super.test(filename, additionalRendererOptions, additionalFiles);
// Generate a schema from the schema, making sure the schemas are the same
// FIXME: We could move this to the superclass and test it for all JSON->X->Y
let schemaSchema = "schema-from-schema.json";
await quicktype({
src: [this.language.output],
srcLang: this.language.name,
lang: this.language.name,
topLevel: this.language.topLevel,
out: schemaSchema,
rendererOptions: {}
});
compareJsonFileToJson({
expectedFile: this.language.output,
given: { file: schemaSchema },
strict: true
});
return 1;
}
}
// These are all inputs where the top-level type is not directly
// converted to TypeScript, mostly arrays.
const skipTypeScriptTests = [
"no-classes.json",
"optional-union.json",
"pokedex.json", // Enums are screwed up: https://github.com/YousefED/typescript-json-schema/issues/186
"github-events.json",
"bug855-short.json",
"bug863.json",
"00c36.json",
"010b1.json",
"050b0.json",
"06bee.json",
"07c75.json",
"0a91a.json",
"10be4.json",
"13d8d.json",
"176f1.json", // Enum screwed up
"1a7f5.json",
"262f0.json", // Enum screwed up
"2df80.json",
"32d5c.json",
"33d2e.json", // Enum screwed up
"34702.json", // Enum screwed up
"3536b.json",
"3e9a3.json", // duplicate top-level type: https://github.com/quicktype/quicktype/issues/726
"3f1ce.json", // Enum screwed up
"43970.json",
"570ec.json",
"5eae5.json",
"65dec.json", // duplicate top-level type
"66121.json",
"6dec6.json", // Enum screwed up
"6eb00.json",
"77392.json",
"7f568.json",
"7eb30.json", // duplicate top-level type
"7fbfb.json",
"9847b.json",
"996bd.json",
"9a503.json",
"9eed5.json",
"a45b0.json",
"ab0d1.json",
"ad8be.json",
"ae9ca.json", // Enum screwed up
"af2d1.json", // Enum screwed up
"b4865.json",
"c8c7e.json",
"cb0cc.json", // Enum screwed up
"cda6c.json",
"dbfb3.json", // Enum screwed up
"e2a58.json",
"e53b5.json",
"e8a0b.json",
"e8b04.json",
"ed095.json", // top-level is a map
"f3139.json",
"f3edf.json",
"f466a.json",
"no-classes.json",
"optional-union.json",
"pokedex.json", // Enums are screwed up: https://github.com/YousefED/typescript-json-schema/issues/186
"github-events.json",
"bug855-short.json",
"bug863.json",
"00c36.json",
"010b1.json",
"050b0.json",
"06bee.json",
"07c75.json",
"0a91a.json",
"10be4.json",
"13d8d.json",
"176f1.json", // Enum screwed up
"1a7f5.json",
"262f0.json", // Enum screwed up
"2df80.json",
"32d5c.json",
"33d2e.json", // Enum screwed up
"34702.json", // Enum screwed up
"3536b.json",
"3e9a3.json", // duplicate top-level type: https://github.com/quicktype/quicktype/issues/726
"3f1ce.json", // Enum screwed up
"43970.json",
"570ec.json",
"5eae5.json",
"65dec.json", // duplicate top-level type
"66121.json",
"6dec6.json", // Enum screwed up
"6eb00.json",
"77392.json",
"7f568.json",
"7eb30.json", // duplicate top-level type
"7fbfb.json",
"9847b.json",
"996bd.json",
"9a503.json",
"9eed5.json",
"a45b0.json",
"ab0d1.json",
"ad8be.json",
"ae9ca.json", // Enum screwed up
"af2d1.json", // Enum screwed up
"b4865.json",
"c8c7e.json",
"cb0cc.json", // Enum screwed up
"cda6c.json",
"dbfb3.json", // Enum screwed up
"e2a58.json",
"e53b5.json",
"e8a0b.json",
"e8b04.json",
"ed095.json", // top-level is a map
"f3139.json",
"f3edf.json",
"f466a.json"
];
class JSONTypeScriptFixture extends JSONToXToYFixture {
constructor(language: languages.Language) {
super("json-ts", "ts", "typescript.ts", { "just-types": "true" }, [], language);
}
constructor(language: languages.Language) {
super("json-ts", "ts", "typescript.ts", { "just-types": "true" }, [], language);
}
shouldSkipTest(sample: Sample): boolean {
if (super.shouldSkipTest(sample)) return true;
return skipTypeScriptTests.indexOf(path.basename(sample.path)) >= 0;
}
shouldSkipTest(sample: Sample): boolean {
if (super.shouldSkipTest(sample)) return true;
return skipTypeScriptTests.indexOf(path.basename(sample.path)) >= 0;
}
}
// This fixture tests generating code from JSON Schema.
class JSONSchemaFixture extends LanguageFixture {
constructor(language: languages.Language, readonly name: string = `schema-${language.name}`) {
super(language);
}
runForName(name: string): boolean {
return this.name === name || name === "schema";
}
getSamples(sources: string[]): { priority: Sample[]; others: Sample[] } {
const prioritySamples = testsInDir("test/inputs/schema/", "schema");
return samplesFromSources(sources, prioritySamples, [], "schema");
}
shouldSkipTest(sample: Sample): boolean {
return _.includes(this.language.skipSchema, path.basename(sample.path));
}
async runQuicktype(filename: string, additionalRendererOptions: RendererOptions): Promise<void> {
await quicktypeForLanguage(this.language, filename, "schema", false, additionalRendererOptions);
}
additionalFiles(sample: Sample): string[] {
const baseName = pathWithoutExtension(sample.path, ".schema");
return additionalTestFiles(baseName, "json", this.language.features);
}
async test(
_sample: string,
additionalRendererOptions: RendererOptions,
additionalFiles: string[]
): Promise<number> {
if (this.language.compileCommand) {
await execAsync(this.language.compileCommand);
constructor(language: languages.Language, readonly name: string = `schema-${language.name}`) {
super(language);
}
if (this.language.runCommand === undefined) return 0;
const failExtensions = this.language.features.map((f) => `.fail.${f}.json`).concat([".fail.json"]);
runForName(name: string): boolean {
return this.name === name || name === "schema";
}
for (const filename of additionalFiles) {
if (failExtensions.some((ext) => filename.endsWith(ext))) {
callAndExpectFailure(
`Expected failure on input ${filename}`,
() =>
exec(
defined(this.language.runCommand)(filename),
runEnvForLanguage(additionalRendererOptions),
false
).stdout
);
} else {
let expected = filename;
for (const feature of this.language.features) {
const featureFilename = filename.replace(".json", `.out.${feature}.json`);
if (fs.existsSync(featureFilename)) {
expected = featureFilename;
break;
}
getSamples(sources: string[]): { priority: Sample[]; others: Sample[] } {
const prioritySamples = testsInDir("test/inputs/schema/", "schema");
return samplesFromSources(sources, prioritySamples, [], "schema");
}
shouldSkipTest(sample: Sample): boolean {
return _.includes(this.language.skipSchema, path.basename(sample.path));
}
async runQuicktype(filename: string, additionalRendererOptions: RendererOptions): Promise<void> {
await quicktypeForLanguage(this.language, filename, "schema", false, additionalRendererOptions);
}
additionalFiles(sample: Sample): string[] {
const baseName = pathWithoutExtension(sample.path, ".schema");
return additionalTestFiles(baseName, "json", this.language.features);
}
async test(
_sample: string,
additionalRendererOptions: RendererOptions,
additionalFiles: string[]
): Promise<number> {
if (this.language.compileCommand) {
await execAsync(this.language.compileCommand);
}
compareJsonFileToJson(comparisonArgs(this.language, filename, expected, additionalRendererOptions));
}
if (this.language.runCommand === undefined) return 0;
const failExtensions = this.language.features.map(f => `.fail.${f}.json`).concat([".fail.json"]);
for (const filename of additionalFiles) {
if (failExtensions.some(ext => filename.endsWith(ext))) {
callAndExpectFailure(
`Expected failure on input ${filename}`,
() =>
exec(
defined(this.language.runCommand)(filename),
runEnvForLanguage(additionalRendererOptions),
false
).stdout
);
} else {
let expected = filename;
for (const feature of this.language.features) {
const featureFilename = filename.replace(".json", `.out.${feature}.json`);
if (fs.existsSync(featureFilename)) {
expected = featureFilename;
break;
}
}
compareJsonFileToJson(comparisonArgs(this.language, filename, expected, additionalRendererOptions));
}
}
return additionalFiles.length;
}
return additionalFiles.length;
}
}
function graphQLSchemaFilename(baseName: string): string {
const baseMatch = baseName.match(/(.*\D)\d+$/);
if (baseMatch === null) {
return failWith("GraphQL test filename does not correspond to naming schema", { baseName });
}
return baseMatch[1] + ".gqlschema";
const baseMatch = baseName.match(/(.*\D)\d+$/);
if (baseMatch === null) {
return failWith("GraphQL test filename does not correspond to naming schema", { baseName });
}
return baseMatch[1] + ".gqlschema";
}
class GraphQLFixture extends LanguageFixture {
constructor(
language: languages.Language,
private readonly _onlyExactName: boolean = false,
readonly name: string = `graphql-${language.name}`
) {
super(language);
}
runForName(name: string): boolean {
return this.name === name || (!this._onlyExactName && name === "graphql");
}
getSamples(sources: string[]): { priority: Sample[]; others: Sample[] } {
const prioritySamples = testsInDir("test/inputs/graphql/", "graphql");
return samplesFromSources(sources, prioritySamples, [], "graphql");
}
shouldSkipTest(_sample: Sample): boolean {
return false;
}
async runQuicktype(filename: string, additionalRendererOptions: RendererOptions): Promise<void> {
const baseName = pathWithoutExtension(filename, ".graphql");
const schemaFilename = graphQLSchemaFilename(baseName);
await quicktypeForLanguage(
this.language,
filename,
"graphql",
false,
additionalRendererOptions,
schemaFilename
);
}
additionalFiles(sample: Sample): string[] {
const baseName = pathWithoutExtension(sample.path, ".graphql");
return additionalTestFiles(baseName, "json");
}
async test(
_filename: string,
additionalRendererOptions: RendererOptions,
additionalFiles: string[]
): Promise<number> {
if (this.language.compileCommand) {
await execAsync(this.language.compileCommand);
constructor(
language: languages.Language,
private readonly _onlyExactName: boolean = false,
readonly name: string = `graphql-${language.name}`
) {
super(language);
}
if (this.language.runCommand === undefined) return 0;
for (const fn of additionalFiles) {
compareJsonFileToJson(comparisonArgs(this.language, fn, fn, additionalRendererOptions));
runForName(name: string): boolean {
return this.name === name || (!this._onlyExactName && name === "graphql");
}
getSamples(sources: string[]): { priority: Sample[]; others: Sample[] } {
const prioritySamples = testsInDir("test/inputs/graphql/", "graphql");
return samplesFromSources(sources, prioritySamples, [], "graphql");
}
shouldSkipTest(_sample: Sample): boolean {
return false;
}
async runQuicktype(filename: string, additionalRendererOptions: RendererOptions): Promise<void> {
const baseName = pathWithoutExtension(filename, ".graphql");
const schemaFilename = graphQLSchemaFilename(baseName);
await quicktypeForLanguage(
this.language,
filename,
"graphql",
false,
additionalRendererOptions,
schemaFilename
);
}
additionalFiles(sample: Sample): string[] {
const baseName = pathWithoutExtension(sample.path, ".graphql");
return additionalTestFiles(baseName, "json");
}
async test(
_filename: string,
additionalRendererOptions: RendererOptions,
additionalFiles: string[]
): Promise<number> {
if (this.language.compileCommand) {
await execAsync(this.language.compileCommand);
}
if (this.language.runCommand === undefined) return 0;
for (const fn of additionalFiles) {
compareJsonFileToJson(comparisonArgs(this.language, fn, fn, additionalRendererOptions));
}
return additionalFiles.length;
}
return additionalFiles.length;
}
}
class CommandSuccessfulLanguageFixture extends LanguageFixture {
constructor(language: languages.Language, public name: string = language.name) {
super(language);
}
runForName(name: string): boolean {
return this.name === name || name === "json";
}
async runQuicktype(sample: string, additionalRendererOptions: RendererOptions): Promise<void> {
// FIXME: add options
await quicktypeForLanguage(this.language, sample, "json", true, additionalRendererOptions);
}
async test(
filename: string,
_additionalRendererOptions: RendererOptions,
_additionalFiles: string[]
): Promise<number> {
if (this.language.compileCommand) {
await execAsync(this.language.compileCommand);
constructor(language: languages.Language, public name: string = language.name) {
super(language);
}
if (this.language.runCommand === undefined) {
throw new Error("Invalid run command.");
runForName(name: string): boolean {
return this.name === name || name === "json";
}
const command = this.language.runCommand(filename);
const results = await execAsync(command);
if (results.stdout.indexOf("Success") === -1) {
throw new Error(`Test failed:\n${results.stdout}`);
async runQuicktype(sample: string, additionalRendererOptions: RendererOptions): Promise<void> {
// FIXME: add options
await quicktypeForLanguage(this.language, sample, "json", true, additionalRendererOptions);
}
return 0;
}
async test(
filename: string,
_additionalRendererOptions: RendererOptions,
_additionalFiles: string[]
): Promise<number> {
if (this.language.compileCommand) {
await execAsync(this.language.compileCommand);
}
shouldSkipTest(sample: Sample): boolean {
if (fs.statSync(sample.path).size > 32 * 1024 * 1024) {
return true;
}
return _.includes(this.language.skipJSON, path.basename(sample.path));
}
if (this.language.runCommand === undefined) {
throw new Error("Invalid run command.");
}
getSamples(sources: string[]): { priority: Sample[]; others: Sample[] } {
// FIXME: this should only run once
const prioritySamples = _.concat(
testsInDir("test/inputs/json/priority", "json"),
testsInDir("test/inputs/json/samples", "json")
);
const command = this.language.runCommand(filename);
const results = await execAsync(command);
const miscSamples = this.language.skipMiscJSON ? [] : testsInDir("test/inputs/json/misc", "json");
if (results.stdout.indexOf("Success") === -1) {
throw new Error(`Test failed:\n${results.stdout}`);
}
let { priority, others } = samplesFromSources(sources, prioritySamples, miscSamples, "json");
const combinationInputs = _.map([1, 2, 3, 4], (n) =>
_.find(prioritySamples, (p) => p.endsWith(`/priority/combinations${n}.json`))
);
if (combinationInputs.some((p) => p === undefined)) {
return failWith("priority/combinations[1234].json samples not found", prioritySamples);
}
if (sources.length === 0 && !ONLY_OUTPUT) {
const quickTestSamples = _.chain(this.language.quickTestRendererOptions)
.flatMap((qt) => {
if (Array.isArray(qt)) {
const [filename, ro] = qt;
const input = _.find(([] as string[]).concat(prioritySamples, miscSamples), (p) =>
p.endsWith(`/${filename}`)
);
if (input === undefined) {
return failWith(`quick-test sample ${filename} not found`, qt);
}
return [
{
path: input,
additionalRendererOptions: ro,
saveOutput: false,
},
];
} else {
return _.map(combinationInputs, (p) => ({
path: defined(p),
additionalRendererOptions: qt,
saveOutput: false,
}));
}
})
.value();
priority = quickTestSamples.concat(priority);
return 0;
}
return { priority, others };
}
shouldSkipTest(sample: Sample): boolean {
if (fs.statSync(sample.path).size > 32 * 1024 * 1024) {
return true;
}
return _.includes(this.language.skipJSON, path.basename(sample.path));
}
getSamples(sources: string[]): { priority: Sample[]; others: Sample[] } {
// FIXME: this should only run once
const prioritySamples = _.concat(
testsInDir("test/inputs/json/priority", "json"),
testsInDir("test/inputs/json/samples", "json")
);
const miscSamples = this.language.skipMiscJSON ? [] : testsInDir("test/inputs/json/misc", "json");
let { priority, others } = samplesFromSources(sources, prioritySamples, miscSamples, "json");
const combinationInputs = _.map([1, 2, 3, 4], n =>
_.find(prioritySamples, p => p.endsWith(`/priority/combinations${n}.json`))
);
if (combinationInputs.some(p => p === undefined)) {
return failWith("priority/combinations[1234].json samples not found", prioritySamples);
}
if (sources.length === 0 && !ONLY_OUTPUT) {
const quickTestSamples = _.chain(this.language.quickTestRendererOptions)
.flatMap(qt => {
if (Array.isArray(qt)) {
const [filename, ro] = qt;
const input = _.find(([] as string[]).concat(prioritySamples, miscSamples), p =>
p.endsWith(`/${filename}`)
);
if (input === undefined) {
return failWith(`quick-test sample ${filename} not found`, qt);
}
return [
{
path: input,
additionalRendererOptions: ro,
saveOutput: false
}
];
} else {
return _.map(combinationInputs, p => ({
path: defined(p),
additionalRendererOptions: qt,
saveOutput: false
}));
}
})
.value();
priority = quickTestSamples.concat(priority);
}
return { priority, others };
}
}
export const allFixtures: Fixture[] = [
// new JSONFixture(languages.CrystalLanguage),
new JSONFixture(languages.CSharpLanguage),
new JSONFixture(languages.CSharpLanguageSystemTextJson, "csharp-SystemTextJson"),
new JSONFixture(languages.JavaLanguage),
new JSONFixture(languages.JavaLanguageWithLegacyDateTime, "java-datetime-legacy"),
new JSONFixture(languages.JavaLanguageWithLombok, "java-lombok"),
new JSONFixture(languages.GoLanguage),
new JSONFixture(languages.CPlusPlusLanguage),
new JSONFixture(languages.RustLanguage),
new JSONFixture(languages.RubyLanguage),
new JSONFixture(languages.PythonLanguage),
new JSONFixture(languages.ElmLanguage),
new JSONFixture(languages.SwiftLanguage),
new JSONFixture(languages.ObjectiveCLanguage),
new JSONFixture(languages.TypeScriptLanguage),
new JSONFixture(languages.FlowLanguage),
new JSONFixture(languages.JavaScriptLanguage),
new JSONFixture(languages.KotlinLanguage),
new JSONFixture(languages.KotlinJacksonLanguage, "kotlin-jackson"),
new JSONFixture(languages.DartLanguage),
new JSONFixture(languages.PikeLanguage),
new JSONFixture(languages.HaskellLanguage),
new JSONSchemaJSONFixture(languages.CSharpLanguage),
new JSONTypeScriptFixture(languages.CSharpLanguage),
// new JSONSchemaFixture(languages.CrystalLanguage),
new JSONSchemaFixture(languages.CSharpLanguage),
new JSONSchemaFixture(languages.JavaLanguage),
new JSONSchemaFixture(languages.JavaLanguageWithLegacyDateTime, "schema-java-datetime-legacy"),
new JSONSchemaFixture(languages.JavaLanguageWithLombok, "schema-java-lombok"),
new JSONSchemaFixture(languages.GoLanguage),
new JSONSchemaFixture(languages.CPlusPlusLanguage),
new JSONSchemaFixture(languages.RustLanguage),
new JSONSchemaFixture(languages.RubyLanguage),
new JSONSchemaFixture(languages.PythonLanguage),
new JSONSchemaFixture(languages.ElmLanguage),
new JSONSchemaFixture(languages.SwiftLanguage),
new JSONSchemaFixture(languages.TypeScriptLanguage),
new JSONSchemaFixture(languages.FlowLanguage),
new JSONSchemaFixture(languages.JavaScriptLanguage),
new JSONSchemaFixture(languages.KotlinLanguage),
new JSONSchemaFixture(languages.KotlinJacksonLanguage, "schema-kotlin-jackson"),
new JSONSchemaFixture(languages.DartLanguage),
new JSONSchemaFixture(languages.PikeLanguage),
new JSONSchemaFixture(languages.HaskellLanguage),
// FIXME: Why are we missing so many language with GraphQL?
new GraphQLFixture(languages.CSharpLanguage),
new GraphQLFixture(languages.JavaLanguage),
new GraphQLFixture(languages.JavaLanguageWithLegacyDateTime, false, "graphql-java-datetime-legacy"),
new GraphQLFixture(languages.JavaLanguageWithLombok, false, "graphql-java-lombok"),
new GraphQLFixture(languages.GoLanguage),
new GraphQLFixture(languages.CPlusPlusLanguage),
new GraphQLFixture(languages.PythonLanguage),
new GraphQLFixture(languages.SwiftLanguage),
new GraphQLFixture(languages.ObjectiveCLanguage, true),
new GraphQLFixture(languages.TypeScriptLanguage),
new GraphQLFixture(languages.FlowLanguage),
new GraphQLFixture(languages.JavaScriptLanguage),
new GraphQLFixture(languages.DartLanguage),
new GraphQLFixture(languages.PikeLanguage),
new GraphQLFixture(languages.HaskellLanguage),
new CommandSuccessfulLanguageFixture(languages.JavaScriptPropTypesLanguage),
// new JSONFixture(languages.CrystalLanguage),
new JSONFixture(languages.CSharpLanguage),
new JSONFixture(languages.CSharpLanguageSystemTextJson, "csharp-SystemTextJson"),
new JSONFixture(languages.JavaLanguage),
new JSONFixture(languages.JavaLanguageWithLegacyDateTime, "java-datetime-legacy"),
new JSONFixture(languages.JavaLanguageWithLombok, "java-lombok"),
new JSONFixture(languages.GoLanguage),
new JSONFixture(languages.CPlusPlusLanguage),
new JSONFixture(languages.RustLanguage),
new JSONFixture(languages.RubyLanguage),
new JSONFixture(languages.PythonLanguage),
new JSONFixture(languages.ElmLanguage),
new JSONFixture(languages.SwiftLanguage),
new JSONFixture(languages.ObjectiveCLanguage),
new JSONFixture(languages.TypeScriptLanguage),
new JSONFixture(languages.FlowLanguage),
new JSONFixture(languages.JavaScriptLanguage),
new JSONFixture(languages.KotlinLanguage),
new JSONFixture(languages.KotlinJacksonLanguage, "kotlin-jackson"),
new JSONFixture(languages.DartLanguage),
new JSONFixture(languages.PikeLanguage),
new JSONFixture(languages.HaskellLanguage),
new JSONSchemaJSONFixture(languages.CSharpLanguage),
new JSONTypeScriptFixture(languages.CSharpLanguage),
// new JSONSchemaFixture(languages.CrystalLanguage),
new JSONSchemaFixture(languages.CSharpLanguage),
new JSONSchemaFixture(languages.JavaLanguage),
new JSONSchemaFixture(languages.JavaLanguageWithLegacyDateTime, "schema-java-datetime-legacy"),
new JSONSchemaFixture(languages.JavaLanguageWithLombok, "schema-java-lombok"),
new JSONSchemaFixture(languages.GoLanguage),
new JSONSchemaFixture(languages.CPlusPlusLanguage),
new JSONSchemaFixture(languages.RustLanguage),
new JSONSchemaFixture(languages.RubyLanguage),
new JSONSchemaFixture(languages.PythonLanguage),
new JSONSchemaFixture(languages.ElmLanguage),
new JSONSchemaFixture(languages.SwiftLanguage),
new JSONSchemaFixture(languages.TypeScriptLanguage),
new JSONSchemaFixture(languages.FlowLanguage),
new JSONSchemaFixture(languages.JavaScriptLanguage),
new JSONSchemaFixture(languages.KotlinLanguage),
new JSONSchemaFixture(languages.KotlinJacksonLanguage, "schema-kotlin-jackson"),
new JSONSchemaFixture(languages.DartLanguage),
new JSONSchemaFixture(languages.PikeLanguage),
new JSONSchemaFixture(languages.HaskellLanguage),
// FIXME: Why are we missing so many language with GraphQL?
new GraphQLFixture(languages.CSharpLanguage),
new GraphQLFixture(languages.JavaLanguage),
new GraphQLFixture(languages.JavaLanguageWithLegacyDateTime, false, "graphql-java-datetime-legacy"),
new GraphQLFixture(languages.JavaLanguageWithLombok, false, "graphql-java-lombok"),
new GraphQLFixture(languages.GoLanguage),
new GraphQLFixture(languages.CPlusPlusLanguage),
new GraphQLFixture(languages.PythonLanguage),
new GraphQLFixture(languages.SwiftLanguage),
new GraphQLFixture(languages.ObjectiveCLanguage, true),
new GraphQLFixture(languages.TypeScriptLanguage),
new GraphQLFixture(languages.FlowLanguage),
new GraphQLFixture(languages.JavaScriptLanguage),
new GraphQLFixture(languages.DartLanguage),
new GraphQLFixture(languages.PikeLanguage),
new GraphQLFixture(languages.HaskellLanguage),
new CommandSuccessfulLanguageFixture(languages.JavaScriptPropTypesLanguage)
];

View File

@ -3,155 +3,155 @@ import { Moment } from "moment";
import { ComparisonRelaxations } from "../utils";
function pathToString(path: string[]): string {
return "." + path.join(".");
return "." + path.join(".");
}
declare namespace Math {
// TypeScript cannot find this function
function fround(n: number): number;
// TypeScript cannot find this function
function fround(n: number): number;
}
function tryParseMoment(s: string): [Moment | undefined, boolean] {
let m = moment(s);
if (m.isValid()) return [m, false];
m = moment(s, "HH:mm:ss.SSZ");
if (m.isValid()) return [m, true];
return [undefined, false];
let m = moment(s);
if (m.isValid()) return [m, false];
m = moment(s, "HH:mm:ss.SSZ");
if (m.isValid()) return [m, true];
return [undefined, false];
}
function momentsEqual(x: Moment, y: Moment, isTime: boolean): boolean {
if (!isTime) {
return x.isSame(y);
}
return (
x.hour() === y.hour() &&
x.minute() === y.minute() &&
x.second() === y.second() &&
x.millisecond() === y.millisecond()
);
if (!isTime) {
return x.isSame(y);
}
return (
x.hour() === y.hour() &&
x.minute() === y.minute() &&
x.second() === y.second() &&
x.millisecond() === y.millisecond()
);
}
// https://stackoverflow.com/questions/1068834/object-comparison-in-javascript
export default function deepEquals(
x: any,
y: any,
assumeStringsEqual: boolean,
relax: ComparisonRelaxations,
path: string[] = []
x: any,
y: any,
assumeStringsEqual: boolean,
relax: ComparisonRelaxations,
path: string[] = []
): boolean {
// remember that NaN === NaN returns false
// and isNaN(undefined) returns true
if (typeof x === "number" && typeof y === "number") {
if (isNaN(x) && isNaN(y)) {
return true;
}
// because sometimes Newtonsoft.JSON is not exact
if (Math.fround(x) === Math.fround(y)) {
return true;
}
console.error(`Numbers are not equal at path ${pathToString(path)}.`);
return false;
}
// Compare primitives and functions.
// Check if both arguments link to the same object.
// Especially useful on the step where we compare prototypes
if (x === y) {
return true;
}
if (typeof x === "string" && typeof y === "string") {
if (assumeStringsEqual || x === y) return true;
const [xMoment, isTime] = tryParseMoment(x);
const [yMoment] = tryParseMoment(y);
if (xMoment !== undefined && yMoment !== undefined && momentsEqual(xMoment, yMoment, isTime)) {
return true;
}
console.error(
`Strings not equal at path ${pathToString(path)}: ${JSON.stringify(x)} !== ${JSON.stringify(y)}.`
);
return false;
}
if (!!relax.allowStringifiedIntegers && typeof x === "string" && typeof y === "number") {
if (x === y.toString()) return true;
console.error(`String and number not equal at path ${pathToString(path)}.`);
return false;
}
if (x instanceof String && y instanceof String) {
if (x.toString() === y.toString()) return true;
console.error(`Number or string not equal at path ${pathToString(path)}.`);
return false;
}
// At last checking prototypes as good as we can
if (!(x instanceof Object && y instanceof Object)) {
console.error(`One is not an object at path ${pathToString(path)}.`);
return false;
}
// If the objects have an own property "constructor" then we need to
// compare it regularly.
if (x.constructor instanceof String && x.constructor !== y.constructor) {
console.error(
`Not the same constructor at path ${pathToString(path)}: should be ${x.constructor} but is ${
y.constructor
}.`
);
return false;
}
if (x.prototype !== y.prototype) {
console.error(`Not the same prototype at path ${pathToString(path)}.`);
return false;
}
if (Array.isArray(x)) {
if (x.length !== y.length) {
console.error(`Arrays don't have the same length at path ${pathToString(path)}.`);
return false;
}
for (let i = 0; i < x.length; i++) {
path.push(i.toString());
if (!deepEquals(x[i], y[i], assumeStringsEqual, relax, path)) {
// remember that NaN === NaN returns false
// and isNaN(undefined) returns true
if (typeof x === "number" && typeof y === "number") {
if (isNaN(x) && isNaN(y)) {
return true;
}
// because sometimes Newtonsoft.JSON is not exact
if (Math.fround(x) === Math.fround(y)) {
return true;
}
console.error(`Numbers are not equal at path ${pathToString(path)}.`);
return false;
}
path.pop();
}
return true;
}
// FIXME: The way we're looking up properties with `indexOf` makes this
// quadratic. So far no problem, so meh.
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
// Compare primitives and functions.
// Check if both arguments link to the same object.
// Especially useful on the step where we compare prototypes
if (x === y) {
return true;
}
for (const p of yKeys) {
// We allow properties in y that aren't present in x
// so long as they're null.
if (xKeys.indexOf(p) < 0) {
if (y[p] !== null) {
console.error(`Non-null property ${p} is not expected at path ${pathToString(path)}.`);
if (typeof x === "string" && typeof y === "string") {
if (assumeStringsEqual || x === y) return true;
const [xMoment, isTime] = tryParseMoment(x);
const [yMoment] = tryParseMoment(y);
if (xMoment !== undefined && yMoment !== undefined && momentsEqual(xMoment, yMoment, isTime)) {
return true;
}
console.error(
`Strings not equal at path ${pathToString(path)}: ${JSON.stringify(x)} !== ${JSON.stringify(y)}.`
);
return false;
}
}
}
for (const p of xKeys) {
if (yKeys.indexOf(p) < 0) {
if (!!relax.allowMissingNull && x[p] === null) {
continue;
}
console.error(`Expected property ${p} not found at path ${pathToString(path)}.`);
return false;
if (!!relax.allowStringifiedIntegers && typeof x === "string" && typeof y === "number") {
if (x === y.toString()) return true;
console.error(`String and number not equal at path ${pathToString(path)}.`);
return false;
}
path.push(p);
if (!deepEquals(x[p], y[p], assumeStringsEqual, relax, path)) {
return false;
if (x instanceof String && y instanceof String) {
if (x.toString() === y.toString()) return true;
console.error(`Number or string not equal at path ${pathToString(path)}.`);
return false;
}
path.pop();
}
return true;
// At last checking prototypes as good as we can
if (!(x instanceof Object && y instanceof Object)) {
console.error(`One is not an object at path ${pathToString(path)}.`);
return false;
}
// If the objects have an own property "constructor" then we need to
// compare it regularly.
if (x.constructor instanceof String && x.constructor !== y.constructor) {
console.error(
`Not the same constructor at path ${pathToString(path)}: should be ${x.constructor} but is ${
y.constructor
}.`
);
return false;
}
if (x.prototype !== y.prototype) {
console.error(`Not the same prototype at path ${pathToString(path)}.`);
return false;
}
if (Array.isArray(x)) {
if (x.length !== y.length) {
console.error(`Arrays don't have the same length at path ${pathToString(path)}.`);
return false;
}
for (let i = 0; i < x.length; i++) {
path.push(i.toString());
if (!deepEquals(x[i], y[i], assumeStringsEqual, relax, path)) {
return false;
}
path.pop();
}
return true;
}
// FIXME: The way we're looking up properties with `indexOf` makes this
// quadratic. So far no problem, so meh.
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
for (const p of yKeys) {
// We allow properties in y that aren't present in x
// so long as they're null.
if (xKeys.indexOf(p) < 0) {
if (y[p] !== null) {
console.error(`Non-null property ${p} is not expected at path ${pathToString(path)}.`);
return false;
}
}
}
for (const p of xKeys) {
if (yKeys.indexOf(p) < 0) {
if (!!relax.allowMissingNull && x[p] === null) {
continue;
}
console.error(`Expected property ${p} not found at path ${pathToString(path)}.`);
return false;
}
path.push(p);
if (!deepEquals(x[p], y[p], assumeStringsEqual, relax, path)) {
return false;
}
path.pop();
}
return true;
}

View File

@ -7,79 +7,79 @@ const exit = require("exit");
const WORKERS = ["👷🏻", "👷🏼", "👷🏽", "👷🏾", "👷🏿"];
export interface ParallelArgs<Item, Result, Acc> {
queue: Item[];
workers: number;
setup(): Promise<Acc>;
map(item: Item, index: number): Promise<Result>;
queue: Item[];
workers: number;
setup(): Promise<Acc>;
map(item: Item, index: number): Promise<Result>;
}
function randomPick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
return arr[Math.floor(Math.random() * arr.length)];
}
function guys(n: number): string {
return _.range(n)
.map(_i => randomPick(WORKERS))
.join(" ");
return _.range(n)
.map(_i => randomPick(WORKERS))
.join(" ");
}
export async function inParallel<Item, Result, Acc>(args: ParallelArgs<Item, Result, Acc>) {
let { queue } = args;
let items = queue.map((item, i) => {
return { item, i };
});
if (cluster.isMaster) {
let { setup, workers, map } = args;
await setup();
cluster.on("message", worker => {
if (items.length) {
worker.send(items.shift());
} else {
worker.kill();
}
let { queue } = args;
let items = queue.map((item, i) => {
return { item, i };
});
cluster.on("exit", (_worker, code, _signal) => {
if (code && code !== 0) {
// Kill workers and exit if any worker dies
_.forIn(cluster.workers, w => {
if (w) {
w.kill();
}
if (cluster.isMaster) {
let { setup, workers, map } = args;
await setup();
cluster.on("message", worker => {
if (items.length) {
worker.send(items.shift());
} else {
worker.kill();
}
});
exit(code);
}
});
console.error(`* Forking ${workers} workers ${guys(workers)}`);
if (workers < 2) {
// We run everything on the master process if only one worker
for (let { item, i } of items) {
await map(item, i);
}
cluster.on("exit", (_worker, code, _signal) => {
if (code && code !== 0) {
// Kill workers and exit if any worker dies
_.forIn(cluster.workers, w => {
if (w) {
w.kill();
}
});
exit(code);
}
});
console.error(`* Forking ${workers} workers ${guys(workers)}`);
if (workers < 2) {
// We run everything on the master process if only one worker
for (let { item, i } of items) {
await map(item, i);
}
} else {
_.range(workers).forEach(i =>
cluster.fork({
worker: i,
// https://github.com/TypeStrong/ts-node/issues/367
TS_NODE_PROJECT: "test/tsconfig.json"
})
);
}
} else {
_.range(workers).forEach(i =>
cluster.fork({
worker: i,
// https://github.com/TypeStrong/ts-node/issues/367
TS_NODE_PROJECT: "test/tsconfig.json"
})
);
// Setup a worker
let { map } = args;
// master sends a { fixtureName, sample } to run
process.on("message", async ({ item, i }) => {
(process.send as any)({
result: await map(item, i)
});
});
// Ask master for work
(process.send as any)("ready");
}
} else {
// Setup a worker
let { map } = args;
// master sends a { fixtureName, sample } to run
process.on("message", async ({ item, i }) => {
(process.send as any)({
result: await map(item, i)
});
});
// Ask master for work
(process.send as any)("ready");
}
}

View File

@ -16,62 +16,60 @@ const CPUs = parseInt(process.env.CPUs || "0", 10) || os.cpus().length;
export type WorkItem = { sample: Sample; fixtureName: string };
async function main(sources: string[]) {
let fixtures = affectedFixtures();
const fixturesFromCmdline = process.env.FIXTURE;
if (fixturesFromCmdline) {
const fixtureNames = fixturesFromCmdline.split(",");
fixtures = _.filter(fixtures, (fixture) => _.some(fixtureNames, (name) => fixture.runForName(name)));
}
let fixtures = affectedFixtures();
const fixturesFromCmdline = process.env.FIXTURE;
if (fixturesFromCmdline) {
const fixtureNames = fixturesFromCmdline.split(",");
fixtures = _.filter(fixtures, fixture => _.some(fixtureNames, name => fixture.runForName(name)));
}
if (allFixtures.length !== fixtures.length) {
console.error(`* Running a subset of fixtures: ${fixtures.map((f) => f.name).join(", ")}`);
}
if (allFixtures.length !== fixtures.length) {
console.error(`* Running a subset of fixtures: ${fixtures.map(f => f.name).join(", ")}`);
}
// Get an array of all { sample, fixtureName } objects we'll run.
// We can't just put the fixture in there because these WorkItems
// will be sent in a message, removing all code.
const samples = _.map(fixtures, (fixture) => ({
fixtureName: fixture.name,
samples: fixture.getSamples(sources),
}));
const priority = _.flatMap(samples, (x) =>
_.map(x.samples.priority, (s) => ({ fixtureName: x.fixtureName, sample: s }))
);
const others = _.flatMap(samples, (x) =>
_.map(x.samples.others, (s) => ({ fixtureName: x.fixtureName, sample: s }))
);
// Get an array of all { sample, fixtureName } objects we'll run.
// We can't just put the fixture in there because these WorkItems
// will be sent in a message, removing all code.
const samples = _.map(fixtures, fixture => ({
fixtureName: fixture.name,
samples: fixture.getSamples(sources)
}));
const priority = _.flatMap(samples, x =>
_.map(x.samples.priority, s => ({ fixtureName: x.fixtureName, sample: s }))
);
const others = _.flatMap(samples, x => _.map(x.samples.others, s => ({ fixtureName: x.fixtureName, sample: s })));
const tests = divideParallelJobs(_.concat(priority, others));
const tests = divideParallelJobs(_.concat(priority, others));
await inParallel({
queue: tests,
workers: CPUs,
await inParallel({
queue: tests,
workers: CPUs,
setup: async () => {
console.error(`* Running ${tests.length} tests between ${fixtures.length} fixtures`);
setup: async () => {
console.error(`* Running ${tests.length} tests between ${fixtures.length} fixtures`);
for (const fixture of fixtures) {
await execAsync(`rm -rf test/runs`);
await execAsync(`mkdir -p test/runs`);
for (const fixture of fixtures) {
await execAsync(`rm -rf test/runs`);
await execAsync(`mkdir -p test/runs`);
await fixture.setup();
}
},
await fixture.setup();
}
},
map: async ({ sample, fixtureName }: WorkItem, index) => {
let fixture = _.find(fixtures, { name: fixtureName }) as Fixture;
try {
await fixture.runWithSample(sample, index, tests.length);
} catch (e) {
console.trace(e);
exit(1);
}
},
});
map: async ({ sample, fixtureName }: WorkItem, index) => {
let fixture = _.find(fixtures, { name: fixtureName }) as Fixture;
try {
await fixture.runWithSample(sample, index, tests.length);
} catch (e) {
console.trace(e);
exit(1);
}
}
});
}
// skip 2 `node` args
main(process.argv.slice(2)).catch((reason) => {
console.error(reason);
process.exit(1);
main(process.argv.slice(2)).catch(reason => {
console.error(reason);
process.exit(1);
});

View File

@ -16,244 +16,239 @@ const DEBUG = process.env.DEBUG !== undefined;
const ASSUME_STRINGS_EQUAL = process.env.ASSUME_STRINGS_EQUAL !== undefined;
export function debug<T>(x: T): T {
if (DEBUG) {
console.log(x);
}
return x;
if (DEBUG) {
console.log(x);
}
return x;
}
export function failWith(message: string, obj: { [key: string]: any }): never {
obj.cwd = process.cwd();
console.error(chalk.red(message));
console.error(chalk.red(JSON.stringify(obj, null, " ")));
throw obj;
obj.cwd = process.cwd();
console.error(chalk.red(message));
console.error(chalk.red(JSON.stringify(obj, null, " ")));
throw obj;
}
function callAndReportFailure<T>(message: string, f: () => T): T | never {
try {
return f();
} catch (e) {
return failWith(message, { error: e });
}
try {
return f();
} catch (e) {
return failWith(message, { error: e });
}
}
export function callAndExpectFailure<T>(message: string, f: () => T): void {
let result: T;
try {
result = f();
} catch {
return;
}
return failWith(message, { result });
let result: T;
try {
result = f();
} catch {
return;
}
return failWith(message, { result });
}
export function exec(
s: string,
env: NodeJS.ProcessEnv | undefined,
printFailure: boolean = true
s: string,
env: NodeJS.ProcessEnv | undefined,
printFailure: boolean = true
): { stdout: string; code: number } {
debug(s);
if (env === undefined) {
env = process.env;
}
const result = shell.exec(s, { silent: !DEBUG, env }) as any;
if (result.code !== 0) {
const failureObj = {
command: s,
code: result.code
};
if (!printFailure) {
throw failureObj;
debug(s);
if (env === undefined) {
env = process.env;
}
console.error(result.stdout);
console.error(result.stderr);
failWith("Command failed", failureObj);
}
const result = shell.exec(s, { silent: !DEBUG, env }) as any;
return result;
if (result.code !== 0) {
const failureObj = {
command: s,
code: result.code
};
if (!printFailure) {
throw failureObj;
}
console.error(result.stdout);
console.error(result.stderr);
failWith("Command failed", failureObj);
}
return result;
}
export function execAsync(s: string, opts: { silent: boolean } = { silent: !DEBUG }) {
return new Promise<{ stdout: string; code: number }>((resolve, reject) => {
debug(s);
shell.exec(s, opts, (code, stdout, stderr) => {
if (code !== 0) {
console.error(stdout);
console.error(stderr);
reject({ command: s, code });
}
resolve({ stdout, code });
return new Promise<{ stdout: string; code: number }>((resolve, reject) => {
debug(s);
shell.exec(s, opts, (code, stdout, stderr) => {
if (code !== 0) {
console.error(stdout);
console.error(stderr);
reject({ command: s, code });
}
resolve({ stdout, code });
});
});
});
}
async function time<T>(work: () => Promise<T>): Promise<[T, number]> {
let start = +new Date();
let result = await work();
let end = +new Date();
return [result, end - start];
let start = +new Date();
let result = await work();
let end = +new Date();
return [result, end - start];
}
// FIXME: This is from build-utils.js. Don't duplicate code.
export function mkdirs(dir: string): void {
const components = dir.split(path.sep);
if (components.length === 0) {
throw new Error("mkdirs must be called with at least one path component");
}
let soFar: string;
if (components[0].length === 0) {
soFar = "/";
components.shift();
} else {
soFar = ".";
}
for (const c of components) {
soFar = path.join(soFar, c);
try {
fs.mkdirSync(soFar);
} catch (e) {
const stat = fs.statSync(soFar);
if (stat.isDirectory()) continue;
throw e;
const components = dir.split(path.sep);
if (components.length === 0) {
throw new Error("mkdirs must be called with at least one path component");
}
let soFar: string;
if (components[0].length === 0) {
soFar = "/";
components.shift();
} else {
soFar = ".";
}
for (const c of components) {
soFar = path.join(soFar, c);
try {
fs.mkdirSync(soFar);
} catch (e) {
const stat = fs.statSync(soFar);
if (stat.isDirectory()) continue;
throw e;
}
}
}
}
export async function quicktype(opts: Partial<CLIOptions>) {
await time(async () => {
await quicktype_(opts);
});
await time(async () => {
await quicktype_(opts);
});
}
export async function quicktypeForLanguage(
language: languages.Language,
sourceFile: string,
sourceLanguage: string,
alphabetizeProperties: boolean,
additionalRendererOptions: RendererOptions,
graphqlSchema?: string
language: languages.Language,
sourceFile: string,
sourceLanguage: string,
alphabetizeProperties: boolean,
additionalRendererOptions: RendererOptions,
graphqlSchema?: string
) {
try {
await quicktype({
srcLang: sourceLanguage,
lang: language.name,
src: [sourceFile],
out: language.output,
graphqlSchema,
topLevel: language.topLevel,
alphabetizeProperties,
rendererOptions: _.merge({}, language.rendererOptions, additionalRendererOptions),
quiet: true,
telemetry: "disable",
// GraphQL input can leave unreachable types in the graph, which means
// their provenance won't be propagated. It does that for non-nullables.
debug: graphqlSchema === undefined ? "provenance" : undefined
});
} catch (e) {
failWith("quicktype threw an exception", {
error: e,
languageName: language.name,
sourceFile,
sourceLanguage,
graphqlSchema,
additionalRendererOptions
});
}
try {
await quicktype({
srcLang: sourceLanguage,
lang: language.name,
src: [sourceFile],
out: language.output,
graphqlSchema,
topLevel: language.topLevel,
alphabetizeProperties,
rendererOptions: _.merge({}, language.rendererOptions, additionalRendererOptions),
quiet: true,
telemetry: "disable",
// GraphQL input can leave unreachable types in the graph, which means
// their provenance won't be propagated. It does that for non-nullables.
debug: graphqlSchema === undefined ? "provenance" : undefined
});
} catch (e) {
failWith("quicktype threw an exception", {
error: e,
languageName: language.name,
sourceFile,
sourceLanguage,
graphqlSchema,
additionalRendererOptions
});
}
}
export async function inDir(dir: string, work: () => Promise<void>) {
let origin = process.cwd();
let origin = process.cwd();
debug(`cd ${dir}`);
process.chdir(dir);
debug(`cd ${dir}`);
process.chdir(dir);
await work();
process.chdir(origin);
await work();
process.chdir(origin);
}
export function testsInDir(dir: string, extension: string): string[] {
return shell.ls(`${dir}/*.${extension}`);
return shell.ls(`${dir}/*.${extension}`);
}
export interface Sample {
path: string;
additionalRendererOptions: RendererOptions;
saveOutput: boolean;
path: string;
additionalRendererOptions: RendererOptions;
saveOutput: boolean;
}
export function samplesFromPaths(paths: string[]): Sample[] {
return paths.map(p => ({ path: p, additionalRendererOptions: {}, saveOutput: true }));
return paths.map(p => ({ path: p, additionalRendererOptions: {}, saveOutput: true }));
}
export function samplesFromSources(
sources: string[],
prioritySamples: string[],
miscSamples: string[],
extension: string
sources: string[],
prioritySamples: string[],
miscSamples: string[],
extension: string
): { priority: Sample[]; others: Sample[] } {
if (sources.length === 0) {
return {
priority: samplesFromPaths(prioritySamples),
others: samplesFromPaths(miscSamples)
};
} else if (sources.length === 1 && fs.lstatSync(sources[0]).isDirectory()) {
return {
priority: samplesFromPaths(testsInDir(sources[0], extension)),
others: []
};
} else {
return { priority: samplesFromPaths(sources), others: [] };
}
if (sources.length === 0) {
return {
priority: samplesFromPaths(prioritySamples),
others: samplesFromPaths(miscSamples)
};
} else if (sources.length === 1 && fs.lstatSync(sources[0]).isDirectory()) {
return {
priority: samplesFromPaths(testsInDir(sources[0], extension)),
others: []
};
} else {
return { priority: samplesFromPaths(sources), others: [] };
}
}
export type ComparisonRelaxations = {
allowMissingNull?: boolean;
allowStringifiedIntegers?: boolean;
allowMissingNull?: boolean;
allowStringifiedIntegers?: boolean;
};
export type FileOrCommand = { file: string } | { command: string; env: NodeJS.ProcessEnv };
function fileOrCommandIsFile(foc: FileOrCommand): foc is { file: string } {
return (foc as any).file !== undefined;
return (foc as any).file !== undefined;
}
export type ComparisonArgs = ComparisonRelaxations & {
expectedFile: string;
given: FileOrCommand;
strict: boolean;
expectedFile: string;
given: FileOrCommand;
strict: boolean;
};
export function compareJsonFileToJson(args: ComparisonArgs) {
debug(args);
debug(args);
const { expectedFile, strict } = args;
const { given } = args;
const { expectedFile, strict } = args;
const { given } = args;
const jsonString = fileOrCommandIsFile(given)
? callAndReportFailure("Could not read JSON output file", () => fs.readFileSync(given.file, "utf8"))
: callAndReportFailure(
"Could not run command for JSON output",
() => exec(given.command, given.env).stdout
);
const jsonString = fileOrCommandIsFile(given)
? callAndReportFailure("Could not read JSON output file", () => fs.readFileSync(given.file, "utf8"))
: callAndReportFailure("Could not run command for JSON output", () => exec(given.command, given.env).stdout);
const givenJSON = callAndReportFailure("Could not parse output JSON", () => JSON.parse(jsonString));
const expectedJSON = callAndReportFailure("Could not read or parse expected JSON file", () =>
JSON.parse(fs.readFileSync(expectedFile, "utf8"))
);
const givenJSON = callAndReportFailure("Could not parse output JSON", () => JSON.parse(jsonString));
const expectedJSON = callAndReportFailure("Could not read or parse expected JSON file", () =>
JSON.parse(fs.readFileSync(expectedFile, "utf8"))
);
let jsonAreEqual = strict
? callAndReportFailure("Failed to strictly compare objects", () =>
strictDeepEquals(givenJSON, expectedJSON)
)
: callAndReportFailure("Failed to compare objects.", () =>
deepEquals(expectedJSON, givenJSON, ASSUME_STRINGS_EQUAL, args)
);
let jsonAreEqual = strict
? callAndReportFailure("Failed to strictly compare objects", () => strictDeepEquals(givenJSON, expectedJSON))
: callAndReportFailure("Failed to compare objects.", () =>
deepEquals(expectedJSON, givenJSON, ASSUME_STRINGS_EQUAL, args)
);
if (!jsonAreEqual) {
failWith("Error: Output is not equivalent to input.", {
expectedFile,
given
});
}
if (!jsonAreEqual) {
failWith("Error: Output is not equivalent to input.", {
expectedFile,
given
});
}
}