Merge pull request #721 from quicktype/typescript-input
TypeScript as a source language
This commit is contained in:
Коммит
ae32fd9486
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "quicktype",
|
||||
"version": "9.0.0",
|
||||
"version": "10.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
@ -32,6 +32,8 @@
|
|||
"pluralize": "^7.0.0",
|
||||
"stream-json": "0.5.2",
|
||||
"string-to-stream": "^1.1.0",
|
||||
"typescript": "~2.6.2",
|
||||
"typescript-json-schema": "^0.21.0",
|
||||
"unicode-properties": "quicktype/unicode-properties#dist",
|
||||
"universal-analytics": "^0.4.16",
|
||||
"urijs": "^1.19.1",
|
||||
|
@ -64,7 +66,6 @@
|
|||
"ts-jest": "^22.4.2",
|
||||
"ts-node": "^3.3.0",
|
||||
"tslint": "^5.8.0",
|
||||
"typescript": "^2.5.3",
|
||||
"uglify-js": "^3.0.26",
|
||||
"watch": "^1.0.2"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
rm -f /tmp/lib.d.ts /tmp/lib.d.ts.gz
|
||||
cp ../data/lib.d.ts /tmp/lib.d.ts
|
||||
gzip -9 /tmp/lib.d.ts
|
||||
echo -n 'export const encodedDefaultTypeScriptLibrary = "'
|
||||
base64 /tmp/lib.d.ts.gz | tr -d '\n'
|
||||
echo '";'
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -470,7 +470,8 @@ export class NewtonsoftCSharpRenderer extends CSharpRenderer {
|
|||
"Serialize",
|
||||
"Newtonsoft",
|
||||
"MetadataPropertyHandling",
|
||||
"DateParseHandling"
|
||||
"DateParseHandling",
|
||||
"FromJson"
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -168,6 +168,10 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
|
|||
}
|
||||
|
||||
export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer {
|
||||
protected forbiddenNamesForGlobalNamespace(): string[] {
|
||||
return ["Array", "Date"];
|
||||
}
|
||||
|
||||
protected deserializerFunctionLine(t: Type, name: Name): Sourcelike {
|
||||
return ["export ", super.deserializerFunctionLine(t, name)];
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
"use strict";
|
||||
|
||||
import { Base64 } from "js-base64";
|
||||
|
||||
import { panic, assert } from "./Support";
|
||||
import { panic, assert, inflateBase64 } from "./Support";
|
||||
import { encodedMarkovChain } from "./EncodedMarkovChain";
|
||||
import * as pako from "pako";
|
||||
|
||||
// This must be null, not undefined, because we read it from JSON.
|
||||
export type SubTrie = number | null | Trie;
|
||||
|
@ -90,8 +88,7 @@ export function train(lines: string[], depth: number): MarkovChain {
|
|||
}
|
||||
|
||||
export function load(): MarkovChain {
|
||||
const bytes = Base64.atob(encodedMarkovChain);
|
||||
return JSON.parse(pako.inflate(bytes, { to: "string" }));
|
||||
return JSON.parse(inflateBase64(encodedMarkovChain));
|
||||
}
|
||||
|
||||
export function evaluateFull(mc: MarkovChain, word: string): [number, number[]] {
|
||||
|
|
|
@ -35,7 +35,7 @@ export function sourcesFromPostmanCollection(
|
|||
}
|
||||
}
|
||||
if (samples.length > 0) {
|
||||
const source: JSONTypeSource = { name: c.name, samples };
|
||||
const source: JSONTypeSource = { kind: "json", name: c.name, samples };
|
||||
const sourceDescription = [c.name];
|
||||
|
||||
if (typeof c.request === "object") {
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
import { Collection, List, Set, isKeyed, isIndexed } from "immutable";
|
||||
|
||||
import { Base64 } from "js-base64";
|
||||
import * as pako from "pako";
|
||||
|
||||
export function intercalate<T>(separator: T, items: Collection<any, T>): List<T> {
|
||||
const acc: T[] = [];
|
||||
items.forEach((x: T) => {
|
||||
|
@ -173,3 +176,8 @@ export async function mapSync<K, V, U>(
|
|||
}
|
||||
return coll.map(_v => results[index++]);
|
||||
}
|
||||
|
||||
export function inflateBase64(encoded: string): string {
|
||||
const bytes = Base64.atob(encoded);
|
||||
return pako.inflate(bytes, { to: "string" });
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
import * as ts from "typescript";
|
||||
import { PartialArgs, CompilerOptions, generateSchema } from "typescript-json-schema";
|
||||
|
||||
import { panic, inflateBase64 } from "./Support";
|
||||
import { encodedDefaultTypeScriptLibrary } from "./EncodedDefaultTypeScriptLibrary";
|
||||
|
||||
const settings: PartialArgs = {
|
||||
required: true,
|
||||
titles: true,
|
||||
topRef: true
|
||||
};
|
||||
|
||||
const compilerOptions: CompilerOptions = {
|
||||
strictNullChecks: true,
|
||||
typeRoots: []
|
||||
};
|
||||
|
||||
const libFileName = "lib.d.ts";
|
||||
let libSource: string | undefined = undefined;
|
||||
|
||||
function getLibSource(): string {
|
||||
if (libSource === undefined) {
|
||||
libSource = inflateBase64(encodedDefaultTypeScriptLibrary);
|
||||
}
|
||||
return libSource;
|
||||
}
|
||||
|
||||
class CompilerHost implements ts.CompilerHost {
|
||||
constructor(_options: ts.CompilerOptions,
|
||||
private readonly _sources: {[fileName: string]: string}) {
|
||||
}
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
if (fileName === libFileName) return true;
|
||||
return Object.prototype.hasOwnProperty.apply(this._sources, fileName);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string | undefined {
|
||||
if (fileName === libFileName) {
|
||||
return getLibSource();
|
||||
}
|
||||
return this._sources[fileName];
|
||||
}
|
||||
|
||||
getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, _onError?: (message: string) => void, _shouldCreateNewSourceFile?: boolean): ts.SourceFile | undefined {
|
||||
const sourceText = this.readFile(fileName);
|
||||
return sourceText !== undefined ? ts.createSourceFile(fileName, sourceText, languageVersion) : undefined;
|
||||
}
|
||||
|
||||
getDefaultLibFileName(_options: CompilerOptions): string {
|
||||
return libFileName;
|
||||
}
|
||||
|
||||
writeFile(_fileName: string, _data: string, _writeByteOrderMark: boolean, _onError: ((message: string) => void) | undefined, _sourceFiles: ReadonlyArray<ts.SourceFile>): void {
|
||||
return panic("cannot write file");
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
return ".";
|
||||
}
|
||||
|
||||
getDirectories(_path: string): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
if (this.useCaseSensitiveFileNames()) {
|
||||
return fileName.toLowerCase();
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
getNewLine(): string {
|
||||
return "\n";
|
||||
}
|
||||
}
|
||||
|
||||
function makeCompilerOptions(jsonCompilerOptions: any, basePath: string = "./"): ts.CompilerOptions {
|
||||
const compilerOptions = ts.convertCompilerOptionsFromJson(jsonCompilerOptions, basePath).options;
|
||||
const options: ts.CompilerOptions = {
|
||||
noEmit: true, emitDecoratorMetadata: true, experimentalDecorators: true, target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
|
||||
};
|
||||
for (const k in compilerOptions) {
|
||||
if (compilerOptions.hasOwnProperty(k)) {
|
||||
options[k] = compilerOptions[k];
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
export function schemaForTypeScriptSources(sourceFileNames: string[]): string;
|
||||
export function schemaForTypeScriptSources(sources: { [fileName: string]: string }): string;
|
||||
export function schemaForTypeScriptSources(sources: string[] | { [fileName: string]: string }): string {
|
||||
const options = makeCompilerOptions(compilerOptions);
|
||||
|
||||
let fileNames: string[];
|
||||
let host: ts.CompilerHost;
|
||||
|
||||
if (Array.isArray(sources)) {
|
||||
/*
|
||||
const sourceContents: {[fileName: string]: string} = {};
|
||||
fileNames = [];
|
||||
|
||||
for (const fileName of sources) {
|
||||
const baseName = path.basename(fileName);
|
||||
sourceContents[baseName] = defined(ts.sys.readFile(fileName));
|
||||
fileNames.push(baseName);
|
||||
}
|
||||
|
||||
host = new CompilerHost(options, sourceContents);
|
||||
*/
|
||||
|
||||
fileNames = sources;
|
||||
host = ts.createCompilerHost(options);
|
||||
} else {
|
||||
fileNames = Object.getOwnPropertyNames(sources);
|
||||
host = new CompilerHost(options, sources);
|
||||
}
|
||||
|
||||
const program = ts.createProgram(fileNames, options, host);
|
||||
const schema = generateSchema(program, "*", settings);
|
||||
return JSON.stringify(schema);
|
||||
}
|
|
@ -11,14 +11,13 @@ import {
|
|||
getTargetLanguage,
|
||||
TypeSource,
|
||||
GraphQLTypeSource,
|
||||
isJSONSource,
|
||||
StringInput,
|
||||
SchemaTypeSource,
|
||||
isSchemaSource,
|
||||
quicktypeMultiFile,
|
||||
SerializedRenderResult,
|
||||
TargetLanguage,
|
||||
languageNamed
|
||||
languageNamed,
|
||||
TypeScriptTypeSource
|
||||
} from "..";
|
||||
|
||||
import { OptionDefinition } from "../RendererOptions";
|
||||
|
@ -33,6 +32,7 @@ import { getStream } from "../get-stream/index";
|
|||
import { train } from "../MarkovChain";
|
||||
import { sourcesFromPostmanCollection } from "../PostmanCollection";
|
||||
import { readableFromFileOrURL, readFromFileOrURL, FetchingJSONSchemaStore } from "./NodeIO";
|
||||
import { schemaForTypeScriptSources } from "../TypeScriptInput";
|
||||
import * as telemetry from "./telemetry";
|
||||
|
||||
const commandLineArgs = require("command-line-args");
|
||||
|
@ -78,7 +78,7 @@ const defaultDefaultTargetLanguageName: string = "go";
|
|||
|
||||
async function sourceFromFileOrUrlArray(name: string, filesOrUrls: string[]): Promise<JSONTypeSource> {
|
||||
const samples = await Promise.all(filesOrUrls.map(readableFromFileOrURL));
|
||||
return { name, samples };
|
||||
return { kind: "json", name, samples };
|
||||
}
|
||||
|
||||
function typeNameFromFilename(filename: string): string {
|
||||
|
@ -108,14 +108,14 @@ async function samplesFromDirectory(dataDir: string, topLevelRefs: string[] | un
|
|||
}
|
||||
|
||||
if (file.endsWith(".url") || file.endsWith(".json")) {
|
||||
// FIXME: Why do we include the URI here?
|
||||
sourcesInDir.push({
|
||||
kind: "json",
|
||||
name,
|
||||
uri: fileOrUrl,
|
||||
samples: [await readableFromFileOrURL(fileOrUrl)]
|
||||
});
|
||||
} else if (file.endsWith(".schema")) {
|
||||
sourcesInDir.push({
|
||||
kind: "schema",
|
||||
name,
|
||||
uri: fileOrUrl,
|
||||
topLevelRefs
|
||||
|
@ -124,7 +124,12 @@ async function samplesFromDirectory(dataDir: string, topLevelRefs: string[] | un
|
|||
assert(graphQLSchema === undefined, `More than one GraphQL schema in ${dataDir}`);
|
||||
graphQLSchema = await readableFromFileOrURL(fileOrUrl);
|
||||
} else if (file.endsWith(".graphql")) {
|
||||
graphQLSources.push({ name, schema: undefined, query: await readableFromFileOrURL(fileOrUrl) });
|
||||
graphQLSources.push({
|
||||
kind: "graphql",
|
||||
name,
|
||||
schema: undefined,
|
||||
query: await readableFromFileOrURL(fileOrUrl)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,25 +156,39 @@ async function samplesFromDirectory(dataDir: string, topLevelRefs: string[] | un
|
|||
let jsonSamples: StringInput[] = [];
|
||||
const schemaSources: SchemaTypeSource[] = [];
|
||||
const graphQLSources: GraphQLTypeSource[] = [];
|
||||
const typeScriptSources: TypeScriptTypeSource[] = [];
|
||||
|
||||
for (const source of await readFilesOrURLsInDirectory(dir)) {
|
||||
// FIXME: We do a type switch here, but we know which types we're putting in
|
||||
// in the function above. It should separate it right away.
|
||||
if (isJSONSource(source)) {
|
||||
jsonSamples = jsonSamples.concat(source.samples);
|
||||
} else if (isSchemaSource(source)) {
|
||||
schemaSources.push(source);
|
||||
} else {
|
||||
graphQLSources.push(source);
|
||||
switch (source.kind) {
|
||||
case "json":
|
||||
jsonSamples = jsonSamples.concat(source.samples);
|
||||
break;
|
||||
case "schema":
|
||||
schemaSources.push(source);
|
||||
break;
|
||||
case "graphql":
|
||||
graphQLSources.push(source);
|
||||
break;
|
||||
case "typescript":
|
||||
typeScriptSources.push(source);
|
||||
break;
|
||||
default:
|
||||
return panic("Unrecognized source");
|
||||
}
|
||||
}
|
||||
if (jsonSamples.length > 0 && schemaSources.length + graphQLSources.length > 0) {
|
||||
return panic("Cannot mix JSON samples with JSON Schema or GraphQL in input subdirectory");
|
||||
|
||||
if (jsonSamples.length > 0 && schemaSources.length + graphQLSources.length + typeScriptSources.length > 0) {
|
||||
return panic("Cannot mix JSON samples with JSON Schems, GraphQL, or TypeScript in input subdirectory");
|
||||
}
|
||||
if (schemaSources.length > 0 && graphQLSources.length > 0) {
|
||||
return panic("Cannot mix JSON Schema with GraphQL in an input subdirectory");
|
||||
|
||||
const oneUnlessEmpty = (xs: any[]) => Math.sign(xs.length);
|
||||
if (oneUnlessEmpty(schemaSources) + oneUnlessEmpty(graphQLSources) + oneUnlessEmpty(typeScriptSources) > 1) {
|
||||
return panic("Cannot mix JSON Schema, GraphQL, and TypeScript in an input subdirectory");
|
||||
}
|
||||
|
||||
if (jsonSamples.length > 0) {
|
||||
sources.push({
|
||||
kind: "json",
|
||||
name: path.basename(dir),
|
||||
samples: jsonSamples
|
||||
});
|
||||
|
@ -221,6 +240,8 @@ export function inferCLIOptions(opts: Partial<CLIOptions>, defaultLanguage?: str
|
|||
"If a GraphQL schema is specified, the source language must be GraphQL"
|
||||
);
|
||||
srcLang = "graphql";
|
||||
} else if (opts.src !== undefined && opts.src.length > 0 && opts.src.every(file => _.endsWith(file, ".ts"))) {
|
||||
srcLang = "typescript";
|
||||
} else {
|
||||
assert(srcLang !== "graphql", "Please specify a GraphQL schema with --graphql-schema or --graphql-introspect");
|
||||
srcLang = withDefault<string>(srcLang, "json");
|
||||
|
@ -306,7 +327,7 @@ function makeOptionDefinitions(targetLanguages: TargetLanguage[]): OptionDefinit
|
|||
alias: "s",
|
||||
type: String,
|
||||
defaultValue: undefined,
|
||||
typeLabel: "json|schema|graphql|postman",
|
||||
typeLabel: "json|schema|graphql|postman|typescript",
|
||||
description: "The source language (default is json)."
|
||||
},
|
||||
{
|
||||
|
@ -594,7 +615,7 @@ async function typeSourceForURIs(name: string, uris: string[], options: CLIOptio
|
|||
return await sourceFromFileOrUrlArray(name, uris);
|
||||
case "schema":
|
||||
assert(uris.length === 1, `Must have exactly one schema for ${name}`);
|
||||
return { name, uri: uris[0], topLevelRefs: topLevelRefsForOptions(options) };
|
||||
return { kind: "schema", name, uri: uris[0], topLevelRefs: topLevelRefsForOptions(options) };
|
||||
default:
|
||||
return panic(`typeSourceForURIs must not be called for source language ${options.srcLang}`);
|
||||
}
|
||||
|
@ -679,7 +700,7 @@ export async function makeQuicktypeOptions(
|
|||
const schema = JSON.parse(schemaString);
|
||||
const query = await readableFromFileOrURL(queryFile);
|
||||
const name = numSources === 1 ? options.topLevel : typeNameFromFilename(queryFile);
|
||||
gqlSources.push({ name, schema, query });
|
||||
gqlSources.push({ kind: "graphql", name, schema, query });
|
||||
}
|
||||
sources = gqlSources;
|
||||
break;
|
||||
|
@ -687,6 +708,17 @@ export async function makeQuicktypeOptions(
|
|||
case "schema":
|
||||
sources = await getSources(options);
|
||||
break;
|
||||
case "typescript":
|
||||
// TODO make this a TypeScriptSource
|
||||
sources = [
|
||||
{
|
||||
kind: "schema",
|
||||
name: options.topLevel,
|
||||
schema: schemaForTypeScriptSources(options.src),
|
||||
topLevelRefs: ["/definitions/"]
|
||||
}
|
||||
];
|
||||
break;
|
||||
case "postman":
|
||||
for (const collectionFile of options.src) {
|
||||
const collectionJSON = fs.readFileSync(collectionFile, "utf8");
|
||||
|
|
46
src/index.ts
46
src/index.ts
|
@ -24,6 +24,7 @@ import { descriptionTypeAttributeKind } from "./TypeAttributes";
|
|||
import { flattenUnions } from "./FlattenUnions";
|
||||
import { resolveIntersections } from "./ResolveIntersections";
|
||||
import { replaceObjectType } from "./ReplaceObjectType";
|
||||
import { schemaForTypeScriptSources } from "./TypeScriptInput";
|
||||
|
||||
// Re-export essential types and functions
|
||||
export { TargetLanguage } from "./TargetLanguage";
|
||||
|
@ -49,16 +50,27 @@ export type RendererOptions = { [name: string]: string };
|
|||
export type StringInput = string | Readable;
|
||||
|
||||
export interface JSONTypeSource {
|
||||
kind: "json";
|
||||
name: string;
|
||||
samples: StringInput[];
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export function isJSONSource(source: TypeSource): source is JSONTypeSource {
|
||||
return "samples" in source;
|
||||
return source.kind === "json";
|
||||
}
|
||||
|
||||
export interface TypeScriptTypeSource {
|
||||
kind: "typescript";
|
||||
sources: { [filename: string]: string };
|
||||
}
|
||||
|
||||
export function isTypeScriptSource(source: TypeSource): source is TypeScriptTypeSource {
|
||||
return source.kind === "typescript";
|
||||
}
|
||||
|
||||
export interface SchemaTypeSource {
|
||||
kind: "schema";
|
||||
name: string;
|
||||
uri?: string;
|
||||
schema?: StringInput;
|
||||
|
@ -66,20 +78,35 @@ export interface SchemaTypeSource {
|
|||
}
|
||||
|
||||
export function isSchemaSource(source: TypeSource): source is SchemaTypeSource {
|
||||
return !("query" in source) && !("samples" in source);
|
||||
return source.kind === "schema";
|
||||
}
|
||||
|
||||
export function toSchemaSource(source: TypeSource): SchemaTypeSource | undefined {
|
||||
if (isSchemaSource(source)) {
|
||||
return source;
|
||||
} else if (isTypeScriptSource(source)) {
|
||||
return {
|
||||
kind: "schema",
|
||||
name: "",
|
||||
schema: schemaForTypeScriptSources(source.sources),
|
||||
topLevelRefs: ["/definitions/"]
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export interface GraphQLTypeSource {
|
||||
kind: "graphql";
|
||||
name: string;
|
||||
schema: any;
|
||||
query: StringInput;
|
||||
}
|
||||
|
||||
export function isGraphQLSource(source: TypeSource): source is GraphQLTypeSource {
|
||||
return "query" in source;
|
||||
return source.kind === "graphql";
|
||||
}
|
||||
|
||||
export type TypeSource = GraphQLTypeSource | JSONTypeSource | SchemaTypeSource;
|
||||
export type TypeSource = GraphQLTypeSource | JSONTypeSource | SchemaTypeSource | TypeScriptTypeSource;
|
||||
|
||||
export interface Options {
|
||||
lang: string | TargetLanguage;
|
||||
|
@ -313,8 +340,11 @@ export class Run {
|
|||
let schemaInputs: Map<string, StringInput> = Map();
|
||||
let schemaSources: List<[uri.URI, SchemaTypeSource]> = List();
|
||||
for (const source of this._options.sources) {
|
||||
if (!isSchemaSource(source)) continue;
|
||||
const { uri, schema } = source;
|
||||
const schemaSource = toSchemaSource(source);
|
||||
|
||||
if (schemaSource === undefined) continue;
|
||||
|
||||
const { uri, schema } = schemaSource;
|
||||
|
||||
let normalizedURI: uri.URI;
|
||||
if (uri === undefined) {
|
||||
|
@ -335,7 +365,7 @@ export class Run {
|
|||
);
|
||||
}
|
||||
|
||||
schemaSources = schemaSources.push([normalizedURI, source]);
|
||||
schemaSources = schemaSources.push([normalizedURI, schemaSource]);
|
||||
}
|
||||
|
||||
if (!schemaSources.isEmpty()) {
|
||||
|
@ -385,7 +415,7 @@ export class Run {
|
|||
this._allInputs.samples[name].description = description;
|
||||
}
|
||||
}
|
||||
} else if (!isSchemaSource(source)) {
|
||||
} else if (!isSchemaSource(source) && !isTypeScriptSource(source)) {
|
||||
assertNever(source);
|
||||
}
|
||||
}
|
||||
|
|
159
test/fixtures.ts
159
test/fixtures.ts
|
@ -267,17 +267,16 @@ class JSONFixture extends LanguageFixture {
|
|||
}
|
||||
}
|
||||
|
||||
// This fixture tests generating Schemas from JSON, then
|
||||
// making sure that they accept the JSON by generating code from
|
||||
// the Schema and running the code on the original JSON. Also
|
||||
// generating a Schema from the Schema and testing that it's
|
||||
// the same as the original Schema.
|
||||
class JSONSchemaJSONFixture extends JSONFixture {
|
||||
// This fixture tests generating code for language X from JSON,
|
||||
// then generating code for Y from the code for X, making sure
|
||||
// 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;
|
||||
|
||||
constructor(language: languages.Language) {
|
||||
const schemaLanguage: languages.Language = {
|
||||
name: "schema",
|
||||
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: (_sample: string) => {
|
||||
|
@ -286,31 +285,54 @@ class JSONSchemaJSONFixture extends JSONFixture {
|
|||
diffViaSchema: false,
|
||||
skipDiffViaSchema: [],
|
||||
allowMissingNull: language.allowMissingNull,
|
||||
output: "schema.json",
|
||||
topLevel: "schema",
|
||||
skipJSON: [
|
||||
"blns-object.json", // AJV refuses to even "compile" the schema we generate
|
||||
"31189.json", // same here
|
||||
"ed095.json" // same here on Travis
|
||||
],
|
||||
output: languageXOutputFilename,
|
||||
topLevel: "TopLevel",
|
||||
skipJSON,
|
||||
skipMiscJSON: false,
|
||||
skipSchema: [],
|
||||
rendererOptions: {},
|
||||
rendererOptions,
|
||||
quickTestRendererOptions: [],
|
||||
sourceFiles: language.sourceFiles
|
||||
};
|
||||
super(schemaLanguage);
|
||||
});
|
||||
this.runLanguage = language;
|
||||
this.name = `schema-json-${language.name}`;
|
||||
this.name = `${this._fixturePrefix}-${language.name}`;
|
||||
}
|
||||
|
||||
runForName(name: string): boolean {
|
||||
return this.name === name || name === "schema-json";
|
||||
return this.name === name || name === this._fixturePrefix;
|
||||
}
|
||||
|
||||
async test(filename: string, additionalRendererOptions: RendererOptions, _additionalFiles: string[]) {
|
||||
// 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({
|
||||
expectedFile: filename,
|
||||
given: { command: this.runLanguage.runCommand(filename) },
|
||||
strict: false,
|
||||
allowMissingNull: this.runLanguage.allowMissingNull
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// This tests generating Schema from JSON, and then generating
|
||||
// target code from that Schema. The target code is then run on
|
||||
// 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
|
||||
"ed095.json" // same here on Travis
|
||||
]
|
||||
super("schema-json", "schema", "schema.json", {}, skipJSON, language);
|
||||
}
|
||||
|
||||
async test(filename: string, additionalRendererOptions: RendererOptions, additionalFiles: string[]) {
|
||||
let input = JSON.parse(fs.readFileSync(filename, "utf8"));
|
||||
let schema = JSON.parse(fs.readFileSync("schema.json", "utf8"));
|
||||
let schema = JSON.parse(fs.readFileSync(this.language.output, "utf8"));
|
||||
|
||||
let ajv = new Ajv({ format: "full" });
|
||||
// Make Ajv's date-time compatible with what we recognize
|
||||
|
@ -322,35 +344,97 @@ class JSONSchemaJSONFixture extends JSONFixture {
|
|||
});
|
||||
}
|
||||
|
||||
// Generate code from the schema
|
||||
await quicktypeForLanguage(this.runLanguage, "schema.json", "schema", false, additionalRendererOptions);
|
||||
|
||||
// Parse the sample with the code generated from its schema, and compare to the sample
|
||||
compareJsonFileToJson({
|
||||
expectedFile: filename,
|
||||
given: { command: this.runLanguage.runCommand(filename) },
|
||||
strict: false,
|
||||
allowMissingNull: this.runLanguage.allowMissingNull
|
||||
});
|
||||
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: ["schema.json"],
|
||||
srcLang: "schema",
|
||||
lang: "schema",
|
||||
topLevel: "schema",
|
||||
src: [this.language.output],
|
||||
srcLang: this.language.name,
|
||||
lang: this.language.name,
|
||||
topLevel: this.language.topLevel,
|
||||
out: schemaSchema,
|
||||
rendererOptions: {}
|
||||
});
|
||||
compareJsonFileToJson({
|
||||
expectedFile: "schema.json",
|
||||
expectedFile: this.language.output,
|
||||
given: { file: schemaSchema },
|
||||
strict: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 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",
|
||||
"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);
|
||||
}
|
||||
|
||||
shouldSkipTest(sample: Sample): boolean {
|
||||
return skipTypeScriptTests.indexOf(path.basename(sample.path)) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This fixture tests generating code from Schema with features
|
||||
// that we can't (yet) get from JSON. Right now that's only
|
||||
// recursive types.
|
||||
|
@ -488,6 +572,7 @@ export const allFixtures: Fixture[] = [
|
|||
new JSONFixture(languages.FlowLanguage),
|
||||
new JSONFixture(languages.JavaScriptLanguage),
|
||||
new JSONSchemaJSONFixture(languages.CSharpLanguage),
|
||||
new JSONTypeScriptFixture(languages.CSharpLanguage),
|
||||
new JSONSchemaFixture(languages.CSharpLanguage),
|
||||
new JSONSchemaFixture(languages.JavaLanguage),
|
||||
new JSONSchemaFixture(languages.GoLanguage),
|
||||
|
|
Загрузка…
Ссылка в новой задаче