Skip to content

Commit

Permalink
Merge pull request loyd#2 from market/feature/701-extended-records
Browse files Browse the repository at this point in the history
MARKETFRONTECH-701: JSON-схема. Поддержать расширенные поля для сущностей: kind , $id и т.д.
  • Loading branch information
B~Vladi authored and GitHub Enterprise committed Nov 21, 2018
2 parents 51d691c + bc2e779 commit e0a0c22
Show file tree
Hide file tree
Showing 29 changed files with 465 additions and 100 deletions.
9 changes: 9 additions & 0 deletions declarations/babel.js
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,14 @@ declare module '@babel/types' {
body: ObjectTypeAnnotation;
}
declare class OpaqueType extends Node {
type: 'OpaqueType';
id: Identifier;
typeParameters: TypeParameterDeclaration | null;
supertype: FlowTypeAnnotation | null;
impltype: FlowTypeAnnotation;
}
declare class DeclareFunction extends Node {
type: 'DeclareFunction';
id: Identifier;
Expand Down Expand Up @@ -1169,6 +1177,7 @@ declare module '@babel/types' {
declare function isArrowFunctionExpression(node: mixed, opts: Object | void): boolean %checks (node instanceof ArrowFunctionExpression);
declare function isClassBody(node: mixed, opts: Object | void): boolean %checks (node instanceof ClassBody);
declare function isClassDeclaration(node: mixed, opts: Object | void): boolean %checks (node instanceof ClassDeclaration);
declare function isOpaqueType(node: mixed, opts: Object | void): boolean %checks (node instanceof OpaqueType);
declare function isClassExpression(node: mixed, opts: Object | void): boolean %checks (node instanceof ClassExpression);
declare function isExportAllDeclaration(node: mixed, opts: Object | void): boolean %checks (node instanceof ExportAllDeclaration);
declare function isExportDefaultDeclaration(node: mixed, opts: Object | void): boolean %checks (node instanceof ExportDefaultDeclaration);
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "flow2schema",
"name": "@yandex-market/flow2schema",
"version": "0.3.2",
"description": "Generate schemas for flowtype definitions",
"author": "Paul Loyd <pavelko95@gmail.com>",
Expand Down Expand Up @@ -53,5 +53,8 @@
"build": "babel src/ -d lib/",
"mocha": "nyc mocha -r @babel/register -R list tests/run.js",
"flow": "flow"
},
"publishConfig": {
"registry": "http://npm.yandex-team.ru/"
}
}
9 changes: 5 additions & 4 deletions src/collector/declarations.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import type {
Block, ClassDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, Identifier,
ImportDeclaration, ImportDefaultSpecifier, ImportSpecifier, InterfaceDeclaration,
Node, TypeAlias, TypeParameterDeclaration, VariableDeclaration, VariableDeclarator,
DeclareTypeAlias, DeclareInterface, DeclareClass,
DeclareTypeAlias, DeclareInterface, DeclareClass, OpaqueType,
} from '@babel/types';

import {
isCallExpression, isClassDeclaration, isClassMethod, isExportDefaultDeclaration, isProgram,
isExportNamedDeclaration, isIdentifier, isImportDeclaration, isImportNamespaceSpecifier,
isImportSpecifier, isInterfaceDeclaration, isObjectPattern, isObjectProperty, isDeclareClass,
isStringLiteral, isTypeAlias, isVariableDeclaration, isDeclareTypeAlias, isDeclareInterface,
isOpaqueType,
} from '@babel/types';

import {invariant} from '../utils';
import Context from './context';
import type {ExternalInfo, TemplateParam} from './query';

Expand Down Expand Up @@ -198,11 +198,12 @@ function processExportDefaultDeclaration(ctx: Context, node: ExportDefaultDeclar
*/

type Declaration = TypeAlias | InterfaceDeclaration | ClassDeclaration
| DeclareTypeAlias | DeclareInterface | DeclareClass;
| DeclareTypeAlias | DeclareInterface | DeclareClass | OpaqueType;

function isDeclaration(node: mixed): boolean %checks {
return isTypeAlias(node) || isInterfaceDeclaration(node) || isClassDeclaration(node)
|| isDeclareTypeAlias(node) || isDeclareInterface(node) || isDeclareClass(node);
|| isDeclareTypeAlias(node) || isDeclareInterface(node) || isDeclareClass(node)
|| isOpaqueType(node);
}

function processDeclaration(ctx: Context, node: Declaration) {
Expand Down
64 changes: 39 additions & 25 deletions src/collector/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ import type {
GenericTypeAnnotation, InterfaceDeclaration, IntersectionTypeAnnotation, TypeAlias,
UnionTypeAnnotation, NullableTypeAnnotation, ObjectTypeIndexer, ObjectTypeProperty,
StringLiteralTypeAnnotation, ObjectTypeAnnotation, AnyTypeAnnotation, MixedTypeAnnotation,
TupleTypeAnnotation, DeclareTypeAlias, DeclareInterface, DeclareClass,
TupleTypeAnnotation, DeclareTypeAlias, DeclareInterface, DeclareClass, OpaqueType,
} from '@babel/types';

import {
isIdentifier, isObjectTypeProperty, isStringLiteralTypeAnnotation, isClassProperty,
isIdentifier, isStringLiteral, isObjectTypeProperty,
isStringLiteralTypeAnnotation, isClassProperty,
} from '@babel/types';

import Context from './context';

import type {
Type, RecordType, Field, ArrayType, TupleType, MapType, UnionType, IntersectionType,
MaybeType, NumberType, StringType, BooleanType, LiteralType, ReferenceType,
Type, RecordType, Field, ArrayType, TupleType, MapType, MaybeType,
} from '../types';

import * as t from '../types';
Expand All @@ -38,6 +38,16 @@ function processTypeAlias(ctx: Context, node: TypeAlias | DeclareTypeAlias) {
ctx.define(name, type);
}

function processOpaqueType(ctx: Context, node: OpaqueType) {
const {name} = node.id;
// TODO: processing supertype annotation.
const type = makeType(ctx, node.impltype);

invariant(type);

ctx.define(name, type);
}

// TODO: type params.
function processInterfaceDeclaration(
ctx: Context,
Expand All @@ -58,10 +68,17 @@ function processInterfaceDeclaration(
for (const extend of node.extends) {
const {name} = extend.id;
const type = ctx.query(name);
let reference;

invariant(type && type.id);
invariant(type);

const reference = t.createReference(t.clone(type.id));
if (type.kind === 'reference') {
reference = type;
} else {
invariant(type.id);

reference = t.createReference(t.clone(type.id));
}

parts.push(reference);
}
Expand Down Expand Up @@ -137,6 +154,8 @@ function makeType(ctx: Context, node: FlowTypeAnnotation): ?Type {
return t.createAny();
case 'MixedTypeAnnotation':
return t.createMixed();
case 'TypeofTypeAnnotation':
return t.createAny();
case 'FunctionTypeAnnotation':
return null;
default:
Expand Down Expand Up @@ -189,27 +208,19 @@ function makeField(ctx: Context, node: ObjectTypeProperty | ClassProperty): ?Fie
return null;
}

let type = null;
const value = isObjectTypeProperty(node) ? node.value : node.typeAnnotation;

// TODO: no type annotation for the class property.
invariant(value);

let type = makeType(ctx, value);

if (node.leadingComments) {
const pragma = (wu: $FlowIssue<4431>)(node.leadingComments)
type = (wu: $FlowIssue<4431>)(node.leadingComments)
.pluck('value')
.map(extractPragmas)
.map(line => extractPragmas(type, line))
.flatten()
.find(pragma => pragma.kind === 'type');

if (pragma) {
type = pragma.value;
}
}

if (!type) {
const value = isObjectTypeProperty(node) ? node.value : node.typeAnnotation;

// TODO: no type annotation for the class property.
invariant(value);

type = makeType(ctx, value);
.toArray()[0];
}

if (!type) {
Expand All @@ -219,10 +230,12 @@ function makeField(ctx: Context, node: ObjectTypeProperty | ClassProperty): ?Fie
// TODO: warning about computed properties.

invariant(isObjectTypeProperty(node) || !node.computed);
invariant(isIdentifier(node.key));
invariant(isIdentifier(node.key) || isStringLiteral(node.key));

const name = isIdentifier(node.key) ? node.key.name : node.key.value;

return {
name: node.key.name,
name,
value: type,
required: node.optional == null || !node.optional,
};
Expand Down Expand Up @@ -300,4 +313,5 @@ export default {
DeclareInterface: processInterfaceDeclaration,
ClassDeclaration: processClassDeclaration,
DeclareClass: processInterfaceDeclaration,
OpaqueType: processOpaqueType,
}
29 changes: 29 additions & 0 deletions src/collector/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ function array(params: (?Type)[]): ?Type {
return t.createArray(t.clone(params[0]));
}

// Error.
function error(params: (?Type)[]): ?Type {
invariant(params.length === 0);

return t.createReference(['Error']);
}

// String.
function string(params: (?Type)[]): ?Type {
invariant(params.length === 0);

return t.createReference(['String']);
}

// Number.
function number(params: (?Type)[]): ?Type {
invariant(params.length === 0);

return t.createReference(['Number']);
}

// $ElementType<T, K> and $PropertyType<T, k>.
function elemType(params: (?Type)[], resolve: TypeId => Type): ?Type {
invariant(params.length === 2);
Expand Down Expand Up @@ -233,10 +254,17 @@ function extDef(params: (?Type)[]): ?Type {
return type;
}

function call(): ?Type {
return t.createAny();
}

export default {
Object: object,
Buffer: buffer,
Array: array,
Error: error,
String: string,
Number: number,
$ReadOnlyArray: array,
$PropertyType: elemType,
$ElementType: elemType,
Expand All @@ -250,6 +278,7 @@ export default {
$All: all,
$Either: either,
$FlowFixMe: fixMe,
$Call: call,

// Extends types
$$extDef: extDef,
Expand Down
18 changes: 10 additions & 8 deletions src/collector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import * as fs from 'fs';
import * as pathlib from 'path';
import wu from 'wu';
import {isNode} from '@babel/types';
import type {Node} from '@babel/types';

Expand All @@ -17,42 +16,45 @@ import Context from './context';
import {invariant} from '../utils';
import type Parser from '../parser';
import type {Type, TypeId} from '../types';
import type {Options} from '../options';
import type {TemplateParam} from './query';

const VISITOR = Object.assign({}, definitionGroup, declarationGroup);

export default class Collector {
+root: string;
+parser: Parser;
+options: Options;
_fund: Fund;
_modules: Map<string, Module>;
_global: Scope;

constructor(parser: Parser, root: string = '.') {
this.root = root;
constructor(parser: Parser, options?: Options = {}) {
this.parser = parser;
this.options = options;
this._fund = new Fund;
this._modules = new Map;
this._global = Scope.global(globals);
}

collect(path: string, internal: boolean = false) {
// TODO: follow symlinks.
path = pathlib.resolve(path);

let module = this._modules.get(path);

if (module) {
return;
}

const lib = this.options.lib;
const libPath: string = lib && lib[path] || path;

// TODO: error wrapping.
const code = fs.readFileSync(path, 'utf8');
const code = fs.readFileSync(libPath, 'utf8');
const ast = this.parser.parse(code);

// TODO: customize it.
// XXX: replace with normal resolver and path-to-id converter.
const id = pathToId(path.replace(/source\.js$/, ''));

module = new Module(id, path);

const scope = this._global.extend(module);
Expand Down Expand Up @@ -104,7 +106,7 @@ export default class Collector {

switch (result.kind) {
case 'external':
const modulePath = scope.resolve(result.info.path);
const modulePath = scope.resolve(result.info.path, this.options.sourceModuleExtensions);

this.collect(modulePath, true);

Expand Down
4 changes: 2 additions & 2 deletions src/collector/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ export default class Module {
return scope.query(reference, params);
}

resolve(path: string): string {
resolve(path: string, extensions: ?string[]): string {
const basedir = pathlib.dirname(this.path);

// TODO: follow symlinks.
return resolve.sync(path, {basedir});
return resolve.sync(path, {basedir, extensions: (extensions || []).concat(['.js'])});
}

exports(): Iterator<[Scope, string]> {
Expand Down

0 comments on commit e0a0c22

Please sign in to comment.