Merge pull request #684 from quicktype/accessor-names

Custom accessor names
This commit is contained in:
Mark Probst 2018-03-23 18:14:54 -07:00 коммит произвёл GitHub
Родитель 558a6e376f 56b2ac539c
Коммит 6f46814ffc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 379 добавлений и 38 удалений

139
src/AccessorNames.ts Normal file
Просмотреть файл

@ -0,0 +1,139 @@
"use strict";
import { Map, Set } from "immutable";
import { TypeAttributeKind, TypeAttributes } from "./TypeAttributes";
import { checkStringMap, isStringMap, defined, assert } from "./Support";
import { EnumType, ClassType, UnionType, Type } from "./Type";
export type AccessorEntry = string | { [language: string]: string };
export type AccessorNames = { [key: string]: AccessorEntry };
export const accessorNamesTypeAttributeKind = new TypeAttributeKind<AccessorNames>(
"accessorNames",
undefined,
_ => undefined,
undefined
);
export function isAccessorEntry(x: any): x is AccessorEntry {
if (typeof x === "string") {
return true;
}
return isStringMap(x, (v: any): v is string => typeof v === "string");
}
export function checkAccessorNames(x: any): AccessorNames {
return checkStringMap(x, isAccessorEntry);
}
// Returns [name, isFixed].
function getFromEntry(entry: AccessorEntry, language: string): [string, boolean] | undefined {
if (typeof entry === "string") return [entry, false];
const maybeForLanguage = entry[language];
if (maybeForLanguage !== undefined) return [maybeForLanguage, true];
const maybeWildcard = entry["*"];
if (maybeWildcard !== undefined) return [maybeWildcard, false];
return undefined;
}
function lookupKey(accessors: AccessorNames, key: string, language: string): [string, boolean] | undefined {
if (!Object.prototype.hasOwnProperty.call(accessors, key)) return undefined;
return getFromEntry(accessors[key], language);
}
export function classPropertyNames(c: ClassType, language: string): Map<string, [string, boolean] | undefined> {
const accessors = accessorNamesTypeAttributeKind.tryGetInAttributes(c.getAttributes());
const map = c.properties;
if (accessors === undefined) return map.map(_ => undefined);
return map.map((_cp, n) => lookupKey(accessors, n, language));
}
export function enumCaseNames(e: EnumType, language: string): Map<string, [string, boolean] | undefined> {
const accessors = accessorNamesTypeAttributeKind.tryGetInAttributes(e.getAttributes());
const map = e.cases.toMap();
if (accessors === undefined) return map.map(_ => undefined);
return map.map(c => lookupKey(accessors, c, language));
}
export function getAccessorName(
names: Map<string, [string, boolean] | undefined>,
original: string
): [string | undefined, boolean] {
const maybeName = names.get(original);
if (maybeName === undefined) return [undefined, false];
return maybeName;
}
export const unionIdentifierTypeAttributeKind = new TypeAttributeKind<Set<number>>(
"unionIdentifier",
(a, b) => a.union(b),
_ => undefined,
undefined
);
let nextUnionIdentifier: number = 0;
export function makeUnionIdentifierAttribute(): TypeAttributes {
const attributes = unionIdentifierTypeAttributeKind.makeAttributes(Set([nextUnionIdentifier]));
nextUnionIdentifier += 1;
return attributes;
}
export const unionMemberNamesTypeAttributeKind = new TypeAttributeKind<Map<number, AccessorEntry>>(
"unionMemberNames",
(a, b) => a.merge(b),
_ => undefined,
undefined
);
export function makeUnionMemberNamesAttribute(unionAttributes: TypeAttributes, entry: AccessorEntry): TypeAttributes {
const identifiers = defined(unionIdentifierTypeAttributeKind.tryGetInAttributes(unionAttributes));
const map = identifiers.toMap().map(_ => entry);
return unionMemberNamesTypeAttributeKind.makeAttributes(map);
}
export function unionMemberName(u: UnionType, member: Type, language: string): [string | undefined, boolean] {
const identifiers = unionIdentifierTypeAttributeKind.tryGetInAttributes(u.getAttributes());
if (identifiers === undefined) return [undefined, false];
const memberNames = unionMemberNamesTypeAttributeKind.tryGetInAttributes(member.getAttributes());
if (memberNames === undefined) return [undefined, false];
let names: Set<string> = Set();
let fixedNames: Set<string> = Set();
identifiers.forEach(i => {
const maybeEntry = memberNames.get(i);
if (maybeEntry === undefined) return;
const maybeName = getFromEntry(maybeEntry, language);
if (maybeName === undefined) return;
const [name, isNameFixed] = maybeName;
if (isNameFixed) {
fixedNames = fixedNames.add(name);
} else {
names = names.add(name);
}
});
let size: number;
let isFixed: boolean;
let first = fixedNames.first();
if (first !== undefined) {
size = fixedNames.size;
isFixed = true;
} else {
first = names.first();
if (first === undefined) return [undefined, false];
size = names.size;
isFixed = false;
}
assert(size === 1, `More than one name given for union member: ${names.join(", ")}`);
return [first, isFixed];
}

Просмотреть файл

@ -29,13 +29,19 @@ import {
descriptionTypeAttributeKind,
propertyDescriptionsTypeAttributeKind
} from "./TypeAttributes";
import { enumCaseNames, classPropertyNames, unionMemberName, getAccessorName } from "./AccessorNames";
const wordWrap: (s: string) => string = require("wordwrap")(90);
const givenNameOrder = 1;
const inferredNameOrder = 3;
const classPropertyNameOrder = 2;
const assignedClassPropertyNameOrder = 1;
const enumCaseNameOrder = 2;
const assignedEnumCaseNameOrder = 1;
const unionMemberNameOrder = 4;
function splitDescription(descriptions: OrderedSet<string> | undefined): string[] | undefined {
@ -307,8 +313,11 @@ export abstract class ConvenienceRenderer extends Renderer {
c: ClassType,
_className: Name,
p: ClassProperty,
jsonName: string
jsonName: string,
assignedName: string | undefined
): Name | undefined {
const namer = this.namerForClassProperty(c, p);
if (namer === null) return undefined;
// FIXME: This alternative should really depend on what the
// actual name of the class ends up being. We can do this
// with a DependencyName.
@ -319,9 +328,9 @@ export abstract class ConvenienceRenderer extends Renderer {
// maybe we'll need global properties for some weird language at
// some point.
const alternative = `${c.getCombinedName()}_${jsonName}`;
const namer = this.namerForClassProperty(c, p);
if (namer === null) return undefined;
return new SimpleName(OrderedSet([jsonName, alternative]), namer, classPropertyNameOrder);
const order = assignedName === undefined ? classPropertyNameOrder : assignedClassPropertyNameOrder;
const names = assignedName === undefined ? [jsonName, alternative] : [assignedName];
return new SimpleName(OrderedSet(names), namer, order);
}
protected makePropertyDependencyNames(
@ -342,9 +351,16 @@ export abstract class ConvenienceRenderer extends Renderer {
let ns: Namespace | undefined;
const accessorNames = classPropertyNames(c, this.targetLanguage.name);
const names = c.sortedProperties
.map((p, jsonName) => {
const name = this.makeNameForProperty(c, className, p, jsonName);
const [assignedName, isFixed] = getAccessorName(accessorNames, jsonName);
let name: Name | undefined;
if (isFixed) {
name = new FixedName(defined(assignedName));
} else {
name = this.makeNameForProperty(c, className, p, jsonName, assignedName);
}
if (name === undefined) return undefined;
if (ns === undefined) {
ns = new Namespace(c.getCombinedName(), this.globalNamespace, forbiddenNamespaces, forbiddenNames);
@ -360,9 +376,14 @@ export abstract class ConvenienceRenderer extends Renderer {
};
protected makeNameForUnionMember(u: UnionType, unionName: Name, t: Type): Name {
return new DependencyName(nonNull(this._unionMemberNamer), unionMemberNameOrder, lookup =>
this.proposeUnionMemberName(u, unionName, t, lookup)
);
const [assignedName, isFixed] = unionMemberName(u, t, this.targetLanguage.name);
if (isFixed) {
return new FixedName(defined(assignedName));
}
return new DependencyName(nonNull(this._unionMemberNamer), unionMemberNameOrder, lookup => {
if (assignedName !== undefined) return assignedName;
return this.proposeUnionMemberName(u, unionName, t, lookup);
});
}
private addUnionMemberNames = (u: UnionType, unionName: Name): void => {
@ -388,11 +409,18 @@ export abstract class ConvenienceRenderer extends Renderer {
defined(this._memberNamesStoreView).set(u, names);
};
protected makeNameForEnumCase(e: EnumType, _enumName: Name, caseName: string): Name {
protected makeNameForEnumCase(
e: EnumType,
_enumName: Name,
caseName: string,
assignedName: string | undefined
): Name {
// FIXME: See the FIXME in `makeNameForProperty`. We do have global
// enum cases, though (in Go), so this is actually useful already.
const alternative = `${e.getCombinedName()}_${caseName}`;
return new SimpleName(OrderedSet([caseName, alternative]), nonNull(this._enumCaseNamer), enumCaseNameOrder);
const order = assignedName === undefined ? enumCaseNameOrder : assignedEnumCaseNameOrder;
const names = assignedName === undefined ? [caseName, alternative] : [assignedName];
return new SimpleName(OrderedSet(names), nonNull(this._enumCaseNamer), order);
}
// FIXME: this is very similar to addPropertyNameds and addUnionMemberNames
@ -411,8 +439,16 @@ export abstract class ConvenienceRenderer extends Renderer {
ns = new Namespace(e.getCombinedName(), this.globalNamespace, forbiddenNamespaces, forbiddenNames);
}
let names = Map<string, Name>();
const accessorNames = enumCaseNames(e, this.targetLanguage.name);
e.cases.forEach(caseName => {
names = names.set(caseName, ns.add(this.makeNameForEnumCase(e, enumName, caseName)));
const [assignedName, isFixed] = getAccessorName(accessorNames, caseName);
let name: Name;
if (isFixed) {
name = new FixedName(defined(assignedName));
} else {
name = this.makeNameForEnumCase(e, enumName, caseName, assignedName);
}
names = names.set(caseName, ns.add(name));
});
defined(this._caseNamesStoreView).set(e, names);
};

Просмотреть файл

@ -14,7 +14,8 @@ import {
defined,
addHashCode,
mapSync,
forEachSync
forEachSync,
checkArray
} from "./Support";
import { TypeGraphBuilder, TypeRef } from "./TypeBuilder";
import { TypeNames } from "./TypeNames";
@ -26,6 +27,13 @@ import {
makeTypeAttributesInferred
} from "./TypeAttributes";
import { JSONSchema, JSONSchemaStore } from "./JSONSchemaStore";
import {
accessorNamesTypeAttributeKind,
checkAccessorNames,
makeUnionIdentifierAttribute,
isAccessorEntry,
makeUnionMemberNamesAttribute
} from "./AccessorNames";
export enum PathElementKind {
Root,
@ -395,6 +403,12 @@ function makeAttributes(schema: StringMap, loc: Location, attributes: TypeAttrib
});
}
function makeNonUnionAccessorAttributes(schema: StringMap): TypeAttributes | undefined {
const maybeAccessors = schema["qt-accessors"];
if (maybeAccessors === undefined) return undefined;
return accessorNamesTypeAttributeKind.makeAttributes(checkAccessorNames(maybeAccessors));
}
function checkTypeList(typeOrTypes: any): OrderedSet<string> {
if (typeof typeOrTypes === "string") {
return OrderedSet([typeOrTypes]);
@ -591,8 +605,28 @@ export async function addTypesInSchema(
}
async function convertOneOrAnyOf(cases: any, kind: string): Promise<TypeRef> {
const maybeAccessors = schema["qt-accessors"];
const unionType = typeBuilder.getUniqueUnionType(makeTypeAttributesInferred(typeAttributes), undefined);
typeBuilder.setSetOperationMembers(unionType, OrderedSet(await makeTypesFromCases(cases, kind)));
const typeRefs = await makeTypesFromCases(cases, kind);
if (maybeAccessors !== undefined) {
const identifierAttribute = makeUnionIdentifierAttribute();
typeBuilder.addAttributes(unionType, identifierAttribute);
const accessors = checkArray(maybeAccessors, isAccessorEntry);
assert(
typeRefs.length === accessors.length,
`Accessor entry array must have the same number of entries as the ${kind}`
);
for (let i = 0; i < typeRefs.length; i++) {
typeBuilder.addAttributes(
typeRefs[i],
makeUnionMemberNamesAttribute(identifierAttribute, accessors[i])
);
}
}
typeBuilder.setSetOperationMembers(unionType, OrderedSet(typeRefs));
return unionType;
}
@ -610,7 +644,7 @@ export async function addTypesInSchema(
predicate = (x: any) => x === null;
break;
case "integer":
predicate = (x: any) => typeof x === "number" && x === Math.floor(x)
predicate = (x: any) => typeof x === "number" && x === Math.floor(x);
break;
default:
predicate = (x: any) => typeof x === name;
@ -652,7 +686,7 @@ export async function addTypesInSchema(
unionTypes.push(await makeArrayType());
}
if (includeObject) {
unionTypes.push(...await makeObjectTypes())
unionTypes.push(...(await makeObjectTypes()));
}
types.push(typeBuilder.getUniqueUnionType(inferredAttributes, OrderedSet(unionTypes)));
@ -673,6 +707,11 @@ export async function addTypesInSchema(
}
if (schema.oneOf !== undefined) {
types.push(await convertOneOrAnyOf(schema.oneOf, "oneOf"));
} else {
const maybeAttributes = makeNonUnionAccessorAttributes(schema);
if (maybeAttributes !== undefined) {
typeBuilder.addAttributes(intersectionType, maybeAttributes);
}
}
if (schema.anyOf !== undefined) {
types.push(await convertOneOrAnyOf(schema.anyOf, "anyOf"));

Просмотреть файл

@ -78,6 +78,7 @@ export default class CPlusPlusTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -254,6 +255,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
private readonly _caseNamingFunction: Namer;
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _justTypes: boolean,
@ -262,7 +264,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
_memberNamingStyle: NamingStyle,
_enumeratorNamingStyle: NamingStyle
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
this._namespaceNames = List(namespaceName.split("::"));

Просмотреть файл

@ -75,6 +75,7 @@ export default class CSharpTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -128,6 +129,7 @@ export class CSharpRenderer extends ConvenienceRenderer {
protected readonly needAttributes: boolean;
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
protected readonly namespaceName: string,
@ -136,7 +138,7 @@ export class CSharpRenderer extends ConvenienceRenderer {
private readonly _useList: boolean,
outputFeatures: OutputFeatures
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
this.needHelpers = outputFeatures.helpers;
this.needAttributes = outputFeatures.attributes;
}

Просмотреть файл

@ -51,6 +51,7 @@ export default class ElmTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -153,13 +154,14 @@ export class ElmRenderer extends ConvenienceRenderer {
private _namedTypeDependents: Map<Name, NamedTypeDependent> = Map();
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _justTypes: boolean,
private readonly _moduleName: string,
private readonly _useList: boolean
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
}
protected forbiddenNamesForGlobalNamespace(): string[] {

Просмотреть файл

@ -49,6 +49,7 @@ export default class GoTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -96,12 +97,13 @@ export class GoRenderer extends ConvenienceRenderer {
private _topLevelUnmarshalNames = Map<Name, Name>();
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _justTypes: boolean,
private readonly _packageName: string
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
}
protected topLevelNameStyle(rawName: string): string {

Просмотреть файл

@ -31,6 +31,7 @@ export default class JSONSchemaTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]

Просмотреть файл

@ -58,6 +58,7 @@ export class JavaTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -183,12 +184,13 @@ export class JavaRenderer extends ConvenienceRenderer {
private _gettersAndSettersForPropertyName: Map<Name, [Name, Name]>;
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _packageName: string,
private readonly _justTypes: boolean
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
this._gettersAndSettersForPropertyName = Map();
}

Просмотреть файл

@ -1,6 +1,6 @@
"use strict";
import { Type, matchType, directlyReachableSingleNamedType, ClassProperty } from "../Type";
import { Type, matchType, directlyReachableSingleNamedType, ClassProperty, ClassType } from "../Type";
import { TypeGraph } from "../TypeGraph";
import {
utf16LegalizeCharacters,
@ -45,6 +45,7 @@ export class JavaScriptTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -96,8 +97,13 @@ function propertyNameStyle(original: string): string {
}
export class JavaScriptRenderer extends ConvenienceRenderer {
constructor(graph: TypeGraph, leadingComments: string[] | undefined, private readonly _runtimeTypecheck: boolean) {
super(graph, leadingComments);
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _runtimeTypecheck: boolean
) {
super(targetLanguage, graph, leadingComments);
}
protected topLevelNameStyle(rawName: string): string {
@ -124,6 +130,17 @@ export class JavaScriptRenderer extends ConvenienceRenderer {
return directlyReachableSingleNamedType(type);
}
protected makeNameForProperty(
c: ClassType,
className: Name,
p: ClassProperty,
jsonName: string,
_assignedName: string | undefined
): Name | undefined {
// Ignore the assigned name
return super.makeNameForProperty(c, className, p, jsonName, undefined);
}
protected emitDescriptionBlock(lines: string[]): void {
this.emitCommentLines(lines, " * ", "/**", " */");
}

Просмотреть файл

@ -72,6 +72,7 @@ export default class ObjectiveCTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -248,6 +249,7 @@ export class ObjectiveCRenderer extends ConvenienceRenderer {
pseudoEnums: Set<EnumType>;
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _justTypes: boolean,
@ -256,7 +258,7 @@ export class ObjectiveCRenderer extends ConvenienceRenderer {
private readonly _extraComments: boolean,
private readonly _marshalingFunctions: boolean
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
// Infer the class prefix from a top-level name if it's not given
if (this._classPrefix === DEFAULT_CLASS_PREFIX) {

Просмотреть файл

@ -75,6 +75,7 @@ export default class RubyTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -124,12 +125,13 @@ function memberNameStyle(original: string): string {
export class RubyRenderer extends ConvenienceRenderer {
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _justTypes: boolean,
private readonly _strictness: Strictness
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
}
protected get commentLineStart(): string {

Просмотреть файл

@ -47,7 +47,11 @@ export default class RustTargetLanguage extends TargetLanguage {
private readonly _deriveDebugOption = new BooleanOption("derive-debug", "Derive Debug impl", false);
protected get rendererClass(): new (graph: TypeGraph, ...optionValues: any[]) => ConvenienceRenderer {
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
...optionValues: any[]
) => ConvenienceRenderer {
return RustRenderer;
}
@ -182,13 +186,14 @@ const rustStringEscape = utf32ConcatMap(escapeNonPrintableMapper(isPrintable, st
export class RustRenderer extends ConvenienceRenderer {
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _density: Density,
private readonly _visibility: Visibility,
private readonly _deriveDebug: boolean
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
}
protected makeNamedTypeNamer(): Namer {

Просмотреть файл

@ -84,6 +84,7 @@ export default class SwiftTargetLanguage extends TargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -229,6 +230,7 @@ export class SwiftRenderer extends ConvenienceRenderer {
private _needNull: boolean = false;
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _justTypes: boolean,
@ -238,7 +240,7 @@ export class SwiftRenderer extends ConvenienceRenderer {
private readonly _convenienceInitializers: boolean,
private readonly _alamofire: boolean
) {
super(graph, leadingComments);
super(targetLanguage, graph, leadingComments);
}
protected forbiddenNamesForGlobalNamespace(): string[] {

Просмотреть файл

@ -10,6 +10,7 @@ import { ConvenienceRenderer } from "../ConvenienceRenderer";
import { BooleanOption, Option } from "../RendererOptions";
import { JavaScriptTargetLanguage, JavaScriptRenderer } from "./JavaScript";
import { defined } from "../Support";
import { TargetLanguage } from "../TargetLanguage";
export abstract class TypeScriptFlowBaseTargetLanguage extends JavaScriptTargetLanguage {
private readonly _justTypes = new BooleanOption("just-types", "Interfaces only", false);
@ -20,6 +21,7 @@ export abstract class TypeScriptFlowBaseTargetLanguage extends JavaScriptTargetL
}
protected abstract get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -32,6 +34,7 @@ export class TypeScriptTargetLanguage extends TypeScriptFlowBaseTargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -44,13 +47,14 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
private readonly _inlineUnions: boolean;
constructor(
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
private readonly _justTypes: boolean,
declareUnions: boolean,
runtimeTypecheck: boolean
) {
super(graph, leadingComments, runtimeTypecheck);
super(targetLanguage, graph, leadingComments, runtimeTypecheck);
this._inlineUnions = !declareUnions;
}
@ -214,6 +218,7 @@ export class FlowTargetLanguage extends TypeScriptFlowBaseTargetLanguage {
}
protected get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]

Просмотреть файл

@ -8,6 +8,7 @@ import { Name, Namespace, assignNames } from "./Naming";
import { Source, Sourcelike, NewlineSource, annotated, sourcelikeToSource, newline } from "./Source";
import { AnnotationData, IssueAnnotationData } from "./Annotation";
import { assert, panic, StringMap } from "./Support";
import { TargetLanguage } from "./TargetLanguage";
export type RenderResult = {
sources: OrderedMap<string, Source>;
@ -46,7 +47,7 @@ export abstract class Renderer {
// @ts-ignore: Initialized in startEmit, which is called from the constructor
private _preventBlankLine: boolean;
constructor(protected readonly typeGraph: TypeGraph, protected readonly leadingComments: string[] | undefined) {
constructor(protected readonly targetLanguage: TargetLanguage, protected readonly typeGraph: TypeGraph, protected readonly leadingComments: string[] | undefined) {
this._finishedFiles = Map();
this.startEmit();
}

Просмотреть файл

@ -17,17 +17,43 @@ export function setUnion<T, TSet extends Set<T>>(a: TSet, b: TSet): TSet {
export type StringMap = { [name: string]: any };
export function checkStringMap(x: any): StringMap {
if (typeof x !== "object") {
return panic(`Value must be an object, but is ${x}`);
export function isStringMap(x: any): x is StringMap;
export function isStringMap<T>(x: any, checkValue: (v: any) => v is T): x is { [name: string]: T };
export function isStringMap<T>(x: any, checkValue?: (v: any) => v is T): boolean {
if (typeof x !== "object" || Array.isArray(x) || x === null) {
return false;
}
return x;
if (checkValue !== undefined) {
for (const k of Object.getOwnPropertyNames(x)) {
const v = x[k];
if (!checkValue(v)) {
return false;
}
}
}
return true;
}
export function checkArray(x: any): any[] {
export function checkStringMap(x: any): StringMap;
export function checkStringMap<T>(x: any, checkValue: (v: any) => v is T): { [name: string]: T };
export function checkStringMap<T>(x: any, checkValue?: (v: any) => v is T): StringMap {
if (isStringMap(x, checkValue as any)) return x;
return panic(`Value must be an object, but is ${x}`);
}
export function checkArray(x: any): any[];
export function checkArray<T>(x: any, checkItem: (v: any) => v is T): T[];
export function checkArray<T>(x: any, checkItem?: (v: any) => v is T): T[] {
if (!Array.isArray(x)) {
return panic(`Value must be an array, but is ${x}`);
}
if (checkItem !== undefined) {
for (const v of x) {
if (!checkItem(v)) {
return panic(`Array item does not satisfy constraint: ${v}`);
}
}
}
return x;
}

Просмотреть файл

@ -29,7 +29,12 @@ export abstract class TargetLanguage {
return { actual, display };
}
get name(): string {
return defined(this.names[0]);
}
protected abstract get rendererClass(): new (
targetLanguage: TargetLanguage,
graph: TypeGraph,
leadingComments: string[] | undefined,
...optionValues: any[]
@ -41,6 +46,7 @@ export abstract class TargetLanguage {
rendererOptions: { [name: string]: any }
): Renderer {
return new this.rendererClass(
this,
graph,
leadingComments,
...this.getOptions().map(o => o.getValue(rendererOptions))

Просмотреть файл

@ -5,10 +5,10 @@ import { Map, OrderedSet, hash } from "immutable";
import { panic, setUnion } from "./Support";
export class TypeAttributeKind<T> {
public readonly combine: (a: T, b: T) => T;
public readonly makeInferred: (a: T) => T;
public readonly makeInferred: (a: T) => (T | undefined);
public readonly stringify: (a: T) => string | undefined;
constructor(readonly name: string, combine: ((a: T, b: T) => T) | undefined, makeInferred: ((a: T) => T) | undefined, stringify: ((a: T) => string | undefined) | undefined) {
constructor(readonly name: string, combine: ((a: T, b: T) => T) | undefined, makeInferred: ((a: T) => (T | undefined)) | undefined, stringify: ((a: T) => string | undefined) | undefined) {
if (combine === undefined) {
combine = () => {
return panic(`Cannot combine type attribute ${name}`);
@ -93,7 +93,7 @@ export function combineTypeAttributes(firstOrArray: TypeAttributes[] | TypeAttri
}
export function makeTypeAttributesInferred(attr: TypeAttributes): TypeAttributes {
return attr.map((value, kind) => kind.makeInferred(value));
return attr.map((value, kind) => kind.makeInferred(value)).filter(v => v !== undefined);
}
export const descriptionTypeAttributeKind = new TypeAttributeKind<OrderedSet<string>>("description", setUnion, a => a, undefined);

Просмотреть файл

@ -0,0 +1,6 @@
{
"union": 123,
"enum": "red",
"foo": "FOO!",
"bar": "BAAAAR"
}

Просмотреть файл

@ -0,0 +1,44 @@
{
"type": "object",
"properties": {
"union": {
"oneOf": [
{
"type": "boolean"
},
{
"type": "number"
}
],
"qt-accessors": ["presence", "amount"]
},
"enum": {
"type": "string",
"enum": ["red", "green", "blue"],
"qt-accessors": {
"red": "fire",
"green": "grass"
}
},
"foo": {
"type": "string"
},
"bar": {
"type": "string"
}
},
"qt-accessors": {
"union": "unionization",
"enum": "enumerification",
"foo": {
"cs": "Fu_uu",
"java": "Goo"
},
"bar": {
"go": "Bah",
"*": "barre"
}
},
"required": ["union", "enum", "foo", "bar"],
"additionalProperties": false
}