Use flow to check code
parent
29ec78a994
commit
94535791e9
1
.babelrc
1
.babelrc
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"presets": [
|
||||
"@babel/flow",
|
||||
["@babel/env", {
|
||||
"targets": {
|
||||
"node": "6.10"
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
[ignore]
|
||||
.*/node_modules/.*
|
||||
.*/tests/samples/.*
|
||||
.*/src/extractors.js
|
||||
|
||||
[libs]
|
||||
declarations/
|
||||
|
||||
[lints]
|
||||
all=warn
|
||||
|
||||
[options]
|
||||
all=true
|
||||
module.use_strict=true
|
||||
munge_underscores=true
|
||||
include_warnings=true
|
||||
unsafe.enable_getters_and_setters=true
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
declare module 'json-stringify-pretty-compact' {
|
||||
declare export default (any, ?{maxLength?: number}) => string;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
declare function describe(string, Function): void;
|
||||
declare function it(string, Function): void;
|
||||
declare function before(Function): void;
|
|
@ -0,0 +1,11 @@
|
|||
declare module 'optimist' {
|
||||
declare type Optimist = {
|
||||
usage(string): Optimist,
|
||||
argv: {
|
||||
[string]: any,
|
||||
_: string[],
|
||||
},
|
||||
};
|
||||
|
||||
declare module.exports: Optimist;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
declare module 'resolve' {
|
||||
declare function sync(string, ?{basedir: string}): string;
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
"flow2schema": "./bin/flow2schema"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.0.0-beta.32",
|
||||
"babylon": "^7.0.0-beta.32",
|
||||
"json-stringify-pretty-compact": "^1.0.4",
|
||||
"optimist": "^0.6.1",
|
||||
|
@ -32,7 +33,9 @@
|
|||
"@babel/cli": "^7.0.0-beta.32",
|
||||
"@babel/core": "^7.0.0-beta.32",
|
||||
"@babel/preset-env": "^7.0.0-beta.32",
|
||||
"@babel/preset-flow": "^7.0.0-beta.32",
|
||||
"@babel/register": "^7.0.0-beta.32",
|
||||
"flow-bin": "^0.59.0",
|
||||
"jasmine": "^2.8.0",
|
||||
"mocha": "^4.0.1",
|
||||
"nyc": "^11.3.0"
|
||||
|
@ -40,6 +43,7 @@
|
|||
"scripts": {
|
||||
"prepare": "npm run build",
|
||||
"build": "babel src/ -d lib/",
|
||||
"test": "nyc mocha -r @babel/register -R list tests/run.js"
|
||||
"test": "flow && nyc mocha -r @babel/register -R list tests/run.js",
|
||||
"flow": "flow"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ const argv = optimist
|
|||
|
||||
argv._.forEach(run);
|
||||
|
||||
function run(path) {
|
||||
function run(path: string) {
|
||||
if (path === '-') {
|
||||
path = '/dev/stdin';
|
||||
}
|
||||
|
|
171
src/collector.js
171
src/collector.js
|
@ -1,34 +1,60 @@
|
|||
import * as assert from 'assert';
|
||||
import * as fs from 'fs';
|
||||
import * as pathlib from 'path';
|
||||
import type {Node} from '@babel/types';
|
||||
|
||||
import globals from './globals';
|
||||
// $FlowFixMe
|
||||
import * as extractors from './extractors';
|
||||
import Command from './commands';
|
||||
import Module from './module';
|
||||
import Scope from './scope';
|
||||
import CircularList from './list';
|
||||
import {isNode} from './utils';
|
||||
import {invariant, isNode} from './utils';
|
||||
import type Parser from './parser';
|
||||
import type {Schema} from './schema';
|
||||
|
||||
type Task = Generator<void, ?Schema, void>;
|
||||
|
||||
type Group = {
|
||||
entries: string[],
|
||||
|
||||
[string]: Node => Generator<any, any, any>,
|
||||
};
|
||||
|
||||
type InstanceParam = {
|
||||
name: string,
|
||||
value: Schema,
|
||||
};
|
||||
|
||||
export default class Collector {
|
||||
constructor(parser, root = '.') {
|
||||
+root: string;
|
||||
+parser: Parser;
|
||||
+schemas: Schema[];
|
||||
taskCount: number;
|
||||
_tasks: CircularList<Task>;
|
||||
_active: boolean;
|
||||
_modules: Map<string, Module>;
|
||||
_roots: Set<Node>;
|
||||
_global: Scope;
|
||||
_running: boolean;
|
||||
|
||||
constructor(parser: Parser, root: string = '.') {
|
||||
this.root = root;
|
||||
this.parser = parser;
|
||||
this.schemas = [];
|
||||
this.tasks = new CircularList;
|
||||
this.taskCount = 0;
|
||||
this.active = true;
|
||||
this.modules = new Map;
|
||||
this.roots = new Set;
|
||||
this.global = Scope.global(globals);
|
||||
this.running = false;
|
||||
this._tasks = new CircularList;
|
||||
this._active = true;
|
||||
this._modules = new Map;
|
||||
this._roots = new Set;
|
||||
this._global = Scope.global(globals);
|
||||
this._running = false;
|
||||
}
|
||||
|
||||
collect(path, internal = false) {
|
||||
collect(path: string, internal: boolean = false) {
|
||||
// TODO: follow symlinks.
|
||||
path = pathlib.resolve(path);
|
||||
|
||||
let module = this.modules.get(path);
|
||||
let module = this._modules.get(path);
|
||||
|
||||
if (module) {
|
||||
return;
|
||||
|
@ -43,18 +69,18 @@ export default class Collector {
|
|||
|
||||
module = new Module(path, namespace);
|
||||
|
||||
const scope = this.global.extend(module);
|
||||
const scope = this._global.extend(module);
|
||||
|
||||
this._freestyle(extractors.declaration, ast.program, scope, null);
|
||||
this._freestyle(extractors.declaration, ast.program, scope, []);
|
||||
|
||||
this.modules.set(path, module);
|
||||
this._modules.set(path, module);
|
||||
|
||||
if (this.running) {
|
||||
if (this._running) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.running = true;
|
||||
this._running = true;
|
||||
this._schedule();
|
||||
|
||||
if (!internal) {
|
||||
|
@ -63,12 +89,12 @@ export default class Collector {
|
|||
this._schedule();
|
||||
}
|
||||
} finally {
|
||||
this.running = false;
|
||||
this._running = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Given the AST output of babylon parse, walk through in a depth-first order.
|
||||
_freestyle(group, root, scope, params) {
|
||||
_freestyle(group: Group, root: Node, scope: Scope, params: InstanceParam[]) {
|
||||
let stack;
|
||||
let parent;
|
||||
let keys = [];
|
||||
|
@ -86,12 +112,13 @@ export default class Collector {
|
|||
continue;
|
||||
}
|
||||
|
||||
// $FlowFixMe
|
||||
const node = parent ? parent[keys[index]] : root;
|
||||
|
||||
if (isNode(node) && isAcceptableGroup(group, node)) {
|
||||
if (!this.roots.has(node)) {
|
||||
if (!this._roots.has(node)) {
|
||||
const task = this._collect(group, node, scope, params);
|
||||
this.roots.add(node);
|
||||
this._roots.add(node);
|
||||
this._spawn(task);
|
||||
}
|
||||
|
||||
|
@ -107,11 +134,12 @@ export default class Collector {
|
|||
} while (stack);
|
||||
}
|
||||
|
||||
* _collect(group, node, scope, params) {
|
||||
* _collect(group: Group, node: Node, scope: Scope, params: InstanceParam[]): Task {
|
||||
// $FlowFixMe
|
||||
const extractor = group[node.type];
|
||||
|
||||
if (!extractor) {
|
||||
this._freestyle(group, node, scope, null);
|
||||
this._freestyle(group, node, scope, []);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -120,7 +148,7 @@ export default class Collector {
|
|||
let result = null;
|
||||
|
||||
while (true) {
|
||||
this.active = true;
|
||||
this._active = true;
|
||||
|
||||
const {done, value} = iter.next(result);
|
||||
|
||||
|
@ -128,49 +156,54 @@ export default class Collector {
|
|||
return value;
|
||||
}
|
||||
|
||||
assert.ok(value);
|
||||
invariant(value);
|
||||
|
||||
if (value instanceof Command) {
|
||||
switch (value.name) {
|
||||
if (isNode(value)) {
|
||||
result = yield* this._collect(group, value, scope, params);
|
||||
} else if (Array.isArray(value)) {
|
||||
result = [];
|
||||
|
||||
for (const val of value) {
|
||||
result.push(yield* this._collect(group, val, scope, params));
|
||||
}
|
||||
} else switch (value.kind) {
|
||||
case 'declare':
|
||||
scope.addDeclaration(...value.data);
|
||||
scope.addDeclaration(value.name, value.node, value.params);
|
||||
|
||||
break;
|
||||
case 'define':
|
||||
const [schema, declared] = value.data;
|
||||
const {schema, declared} = value;
|
||||
|
||||
if (declared && params) {
|
||||
if (declared && params.length > 0) {
|
||||
const name = schema.name;
|
||||
|
||||
schema.name = generateGenericName(name, params);
|
||||
|
||||
scope.addInstance(name, schema, params.map(p => p.value));
|
||||
} else {
|
||||
scope.addDefinition(...value.data);
|
||||
scope.addDefinition(schema, declared);
|
||||
}
|
||||
|
||||
this.schemas.push(schema);
|
||||
|
||||
break;
|
||||
case 'external':
|
||||
scope.addImport(value.data);
|
||||
scope.addImport(value.external);
|
||||
|
||||
break;
|
||||
case 'provide':
|
||||
scope.addExport(...value.data);
|
||||
scope.addExport(value.name, value.reference);
|
||||
|
||||
break;
|
||||
case 'query':
|
||||
if (params) {
|
||||
const param = params.find(p => p.name === value.data[0]);
|
||||
const param = params.find(p => p.name === value.name);
|
||||
|
||||
if (param) {
|
||||
// TODO: warning about missing param.
|
||||
result = param.value;
|
||||
break;
|
||||
} else {
|
||||
result = yield* this._query(scope, value.name, value.params);
|
||||
}
|
||||
}
|
||||
|
||||
result = yield* this._query(scope, ...value.data);
|
||||
|
||||
break;
|
||||
case 'enter':
|
||||
|
@ -178,7 +211,7 @@ export default class Collector {
|
|||
|
||||
break;
|
||||
case 'exit':
|
||||
assert.ok(scope.parent);
|
||||
invariant(scope.parent);
|
||||
scope = scope.parent;
|
||||
|
||||
break;
|
||||
|
@ -187,24 +220,14 @@ export default class Collector {
|
|||
|
||||
break;
|
||||
}
|
||||
} else if (Array.isArray(value)) {
|
||||
result = [];
|
||||
|
||||
for (const val of value) {
|
||||
result.push(yield* this._collect(group, val, scope, params));
|
||||
}
|
||||
} else {
|
||||
assert.ok(isNode(value));
|
||||
result = yield* this._collect(group, value, scope, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* _query(scope, name, params) {
|
||||
* _query(scope: Scope, name: string, params: Schema[]): Task {
|
||||
let result = scope.query(name, params);
|
||||
|
||||
// TODO: warning.
|
||||
assert.notEqual(result.type, 'unknown');
|
||||
invariant(result.type !== 'unknown');
|
||||
|
||||
// Resulting scope is always the best choice for waiting.
|
||||
scope = result.scope;
|
||||
|
@ -218,7 +241,10 @@ export default class Collector {
|
|||
|
||||
this.collect(modulePath, true);
|
||||
|
||||
const module = this.modules.get(modulePath);
|
||||
const module = this._modules.get(modulePath);
|
||||
|
||||
invariant(module);
|
||||
|
||||
const {imported} = result.info;
|
||||
|
||||
while ((result = module.query(imported, params)).type === 'unknown') {
|
||||
|
@ -230,7 +256,7 @@ export default class Collector {
|
|||
}
|
||||
|
||||
// TODO: reexports.
|
||||
assert.ok(result.type === 'declaration' || result.type === 'template');
|
||||
invariant(result.type === 'declaration' || result.type === 'template');
|
||||
|
||||
scope = result.scope;
|
||||
name = result.name;
|
||||
|
@ -238,23 +264,27 @@ export default class Collector {
|
|||
// Fallthrough.
|
||||
case 'declaration':
|
||||
case 'template':
|
||||
let tmplParams = null;
|
||||
const tmplParams = [];
|
||||
|
||||
if (result.type === 'template') {
|
||||
tmplParams = result.params.map((p, i) => ({
|
||||
for (const [i, p] of result.params.entries()) {
|
||||
tmplParams.push({
|
||||
name: p.name,
|
||||
value: params[i] || p.default,
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
invariant(result.type === 'declaration' || result.type === 'template');
|
||||
|
||||
this._freestyle(extractors.definition, result.node, scope, tmplParams);
|
||||
|
||||
while ((result = scope.query(name, params)).type !== 'definition') {
|
||||
assert.notEqual(result.type, 'external');
|
||||
invariant(result.type !== 'external');
|
||||
yield;
|
||||
}
|
||||
|
||||
assert.equal(result.type, 'definition');
|
||||
invariant(result.type === 'definition');
|
||||
|
||||
// Fallthrough.
|
||||
case 'definition':
|
||||
|
@ -262,19 +292,19 @@ export default class Collector {
|
|||
}
|
||||
}
|
||||
|
||||
* _grabExports(module) {
|
||||
* _grabExports(module: Module): Task {
|
||||
for (const [scope, name] of module.exports()) {
|
||||
yield* this._query(scope, name, null);
|
||||
yield* this._query(scope, name, []);
|
||||
}
|
||||
}
|
||||
|
||||
_spawn(task) {
|
||||
this.tasks.add(task);
|
||||
_spawn(task: Task) {
|
||||
this._tasks.add(task);
|
||||
++this.taskCount;
|
||||
}
|
||||
|
||||
_schedule() {
|
||||
const {tasks} = this;
|
||||
const tasks = this._tasks;
|
||||
|
||||
let marker = null;
|
||||
|
||||
|
@ -290,9 +320,9 @@ export default class Collector {
|
|||
|
||||
tasks.add(task);
|
||||
|
||||
if (this.active) {
|
||||
if (this._active) {
|
||||
marker = task;
|
||||
this.active = false;
|
||||
this._active = false;
|
||||
} else if (task === marker) {
|
||||
// TODO: warning.
|
||||
return;
|
||||
|
@ -301,7 +331,7 @@ export default class Collector {
|
|||
}
|
||||
}
|
||||
|
||||
function pathToNamespace(path) {
|
||||
function pathToNamespace(path: string): string {
|
||||
const pathObj = pathlib.parse(path);
|
||||
|
||||
return pathlib.format({
|
||||
|
@ -313,15 +343,16 @@ function pathToNamespace(path) {
|
|||
.join('.');
|
||||
}
|
||||
|
||||
function isAcceptableGroup(group, node) {
|
||||
function isAcceptableGroup(group: Group, node: Node): boolean {
|
||||
// $FlowFixMe
|
||||
return group.entries.includes(node.type);
|
||||
}
|
||||
|
||||
function generateGenericName(base, params) {
|
||||
function generateGenericName(base: string, params: InstanceParam[]): string {
|
||||
let name = base + '_';
|
||||
|
||||
for (const {value} of params) {
|
||||
assert.equal(typeof value, 'string');
|
||||
invariant(typeof value === 'string');
|
||||
name += '_' + value;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +1,66 @@
|
|||
export default class Command {
|
||||
constructor(name, data) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
import type {Node} from '@babel/types';
|
||||
|
||||
import type {Schema} from './schema';
|
||||
import type {TemplateParam, ExternalInfo} from './query';
|
||||
|
||||
export type Command =
|
||||
| {kind: 'declare', name: string, node: Node, params: TemplateParam[]}
|
||||
| {kind: 'define', schema: Schema, declared: boolean}
|
||||
| {kind: 'external', external: ExternalInfo}
|
||||
| {kind: 'provide', name: string, reference: string}
|
||||
| {kind: 'query', name: string, params: Schema[]}
|
||||
| {kind: 'enter'}
|
||||
| {kind: 'exit'}
|
||||
| {kind: 'namespace'};
|
||||
|
||||
export function declare(name: string, node: Node, params: ?TemplateParam[]): Command {
|
||||
return {
|
||||
kind: 'declare',
|
||||
name,
|
||||
node,
|
||||
params: params || [],
|
||||
};
|
||||
}
|
||||
|
||||
export function declare(name, node, params) {
|
||||
return new Command('declare', [name, node, params]);
|
||||
export function define(schema: Schema, declared: boolean = true): Command {
|
||||
return {
|
||||
kind: 'define',
|
||||
schema,
|
||||
declared,
|
||||
};
|
||||
}
|
||||
|
||||
export function define(schema, declared = true) {
|
||||
return new Command('define', [schema, declared]);
|
||||
export function external(external: ExternalInfo): Command {
|
||||
return {
|
||||
kind: 'external',
|
||||
external,
|
||||
};
|
||||
}
|
||||
|
||||
export function external(external) {
|
||||
return new Command('external', external);
|
||||
export function provide(name: string, reference: string = name): Command {
|
||||
return {
|
||||
kind: 'provide',
|
||||
name,
|
||||
reference,
|
||||
};
|
||||
}
|
||||
|
||||
export function provide(name, reference = name) {
|
||||
return new Command('provide', [name, reference]);
|
||||
export function query(name: string, params: ?Schema[]): Command {
|
||||
return {
|
||||
kind: 'query',
|
||||
name,
|
||||
params: params || [],
|
||||
};
|
||||
}
|
||||
|
||||
export function query(name, params = null) {
|
||||
return new Command('query', [name, params]);
|
||||
export function enter(): Command {
|
||||
return {kind: 'enter'};
|
||||
}
|
||||
|
||||
export function enter() {
|
||||
return new Command('enter');
|
||||
export function exit(): Command {
|
||||
return {kind: 'exit'};
|
||||
}
|
||||
|
||||
export function exit() {
|
||||
return new Command('exit');
|
||||
}
|
||||
|
||||
export function namespace() {
|
||||
return new Command('namespace');
|
||||
export function namespace(): Command {
|
||||
return {kind: 'namespace'};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import * as assert from 'assert';
|
||||
import * as t from '@babel/types';
|
||||
|
||||
import {declare, define, external, provide, query, enter, exit, namespace} from './commands';
|
||||
import {partition, isNode} from './utils';
|
||||
import {invariant, partition} from './utils';
|
||||
import type {Schema} from './schema';
|
||||
|
||||
type E = Generator<any, any, any>;
|
||||
|
||||
export const definition = {
|
||||
entries: [
|
||||
|
@ -10,7 +13,7 @@ export const definition = {
|
|||
'ClassDeclaration',
|
||||
],
|
||||
|
||||
* TypeAlias(node) {
|
||||
* TypeAlias(node: t.TypeAlias): E {
|
||||
let schema = yield node.right;
|
||||
|
||||
if (typeof schema === 'string') {
|
||||
|
@ -25,7 +28,7 @@ export const definition = {
|
|||
return schema;
|
||||
},
|
||||
|
||||
* InterfaceDeclaration(node) {
|
||||
* InterfaceDeclaration(node: t.InterfaceDeclaration): E {
|
||||
let schema = yield node.body;
|
||||
|
||||
if (node.extends.length > 0) {
|
||||
|
@ -51,7 +54,7 @@ export const definition = {
|
|||
return schema;
|
||||
},
|
||||
|
||||
* ClassDeclaration(node) {
|
||||
* ClassDeclaration(node: t.ClassDeclaration): E {
|
||||
let schema = yield node.body;
|
||||
|
||||
if (node.superClass) {
|
||||
|
@ -69,14 +72,14 @@ export const definition = {
|
|||
return schema;
|
||||
},
|
||||
|
||||
* ClassBody(node) {
|
||||
* ClassBody(node: t.ClassBody): E {
|
||||
return {
|
||||
type: 'record',
|
||||
fields: (yield node.body).filter(Boolean),
|
||||
};
|
||||
},
|
||||
|
||||
* ClassProperty(node) {
|
||||
* ClassProperty(node: t.ClassProperty): E {
|
||||
if (node.static) {
|
||||
return null;
|
||||
}
|
||||
|
@ -84,17 +87,17 @@ export const definition = {
|
|||
return yield* extractProperty(node, node.typeAnnotation);
|
||||
},
|
||||
|
||||
* ClassMethod(node) {
|
||||
* ClassMethod(node: t.ClassMethod): E {
|
||||
return null;
|
||||
},
|
||||
|
||||
* ObjectTypeAnnotation(node) {
|
||||
* ObjectTypeAnnotation(node: t.ObjectTypeAnnotation): E {
|
||||
if (node.indexers.length > 0) {
|
||||
// Allow functions, getters and setters.
|
||||
const properties = (yield node.properties).filter(Boolean);
|
||||
|
||||
assert.equal(properties.length, 0);
|
||||
assert.equal(node.indexers.length, 1);
|
||||
invariant(properties.length === 0);
|
||||
invariant(node.indexers.length === 1);
|
||||
|
||||
return {
|
||||
type: 'map',
|
||||
|
@ -108,42 +111,42 @@ export const definition = {
|
|||
};
|
||||
},
|
||||
|
||||
* ObjectTypeProperty(node) {
|
||||
* ObjectTypeProperty(node: t.ObjectTypeProperty): E {
|
||||
return yield* extractProperty(node, node.value);
|
||||
},
|
||||
|
||||
* ObjectTypeIndexer(node) {
|
||||
* ObjectTypeIndexer(node: t.ObjectTypeIndexer): E {
|
||||
const key = yield node.key;
|
||||
|
||||
assert.equal(key, 'string');
|
||||
invariant(key === 'string');
|
||||
|
||||
return yield node.value;
|
||||
},
|
||||
|
||||
* TypeAnnotation(node) {
|
||||
* TypeAnnotation(node: t.TypeAnnotation): E {
|
||||
return yield node.typeAnnotation;
|
||||
},
|
||||
|
||||
* NumberTypeAnnotation(node) {
|
||||
* NumberTypeAnnotation(node: t.NumberTypeAnnotation): E {
|
||||
return 'double';
|
||||
},
|
||||
|
||||
* StringTypeAnnotation(node) {
|
||||
* StringTypeAnnotation(node: t.StringTypeAnnotation): E {
|
||||
return 'string';
|
||||
},
|
||||
|
||||
* BooleanTypeAnnotation(node) {
|
||||
* BooleanTypeAnnotation(node: t.BooleanTypeAnnotation): E {
|
||||
return 'boolean';
|
||||
},
|
||||
|
||||
* ArrayTypeAnnotation(node) {
|
||||
* ArrayTypeAnnotation(node: t.ArrayTypeAnnotation): E {
|
||||
return {
|
||||
type: 'array',
|
||||
items: yield node.elementType,
|
||||
};
|
||||
},
|
||||
|
||||
* UnionTypeAnnotation(node) {
|
||||
* UnionTypeAnnotation(node: t.UnionTypeAnnotation): E {
|
||||
// TODO: flatten variants.
|
||||
|
||||
let [symbols, variants] = partition(node.types, isEnumSymbol);
|
||||
|
@ -167,10 +170,11 @@ export const definition = {
|
|||
return variants;
|
||||
},
|
||||
|
||||
* IntersectionTypeAnnotation(node) {
|
||||
* IntersectionTypeAnnotation(node: t.IntersectionTypeAnnotation): E {
|
||||
const schemas = [];
|
||||
|
||||
for (const type of node.types) {
|
||||
// TODO: support arbitrary types, not only references.
|
||||
const name = yield type;
|
||||
const schema = yield query(name);
|
||||
|
||||
|
@ -180,22 +184,22 @@ export const definition = {
|
|||
return mergeSchemas(schemas);
|
||||
},
|
||||
|
||||
* NullableTypeAnnotation(node) {
|
||||
* NullableTypeAnnotation(node: t.NullableTypeAnnotation): E {
|
||||
return ['null', yield node.typeAnnotation];
|
||||
},
|
||||
|
||||
* NullLiteralTypeAnnotation(node) {
|
||||
* NullLiteralTypeAnnotation(node: t.NullLiteralTypeAnnotation): E {
|
||||
return 'null';
|
||||
},
|
||||
|
||||
* StringLiteralTypeAnnotation(node) {
|
||||
* StringLiteralTypeAnnotation(node: t.StringLiteralTypeAnnotation): E {
|
||||
return {
|
||||
type: 'enum',
|
||||
symbols: [node.value],
|
||||
};
|
||||
},
|
||||
|
||||
* GenericTypeAnnotation(node) {
|
||||
* GenericTypeAnnotation(node: t.GenericTypeAnnotation): E {
|
||||
const name = yield node.id;
|
||||
const params = node.typeParameters && (yield node.typeParameters);
|
||||
|
||||
|
@ -218,27 +222,27 @@ export const definition = {
|
|||
return makeFullname(schema);
|
||||
},
|
||||
|
||||
* TypeParameterInstantiation(node) {
|
||||
* TypeParameterInstantiation(node: t.TypeParameterInstantiation): E {
|
||||
return yield node.params;
|
||||
},
|
||||
|
||||
* FunctionTypeAnnotation(node) {
|
||||
* FunctionTypeAnnotation(node: t.FunctionTypeAnnotation): E {
|
||||
return null;
|
||||
},
|
||||
|
||||
* InterfaceExtends(node) {
|
||||
* InterfaceExtends(node: t.InterfaceExtends): E {
|
||||
return yield node.id;
|
||||
},
|
||||
|
||||
* Identifier(node) {
|
||||
* Identifier(node: t.Identifier): E {
|
||||
return node.name;
|
||||
},
|
||||
|
||||
* CommentLine(node) {
|
||||
* CommentLine(node: t.CommentLine): E {
|
||||
return extractPragma(node.value);
|
||||
},
|
||||
|
||||
* CommentBlock(node) {
|
||||
* CommentBlock(node: t.CommentBlock): E {
|
||||
return extractPragma(node.value);
|
||||
},
|
||||
};
|
||||
|
@ -260,11 +264,11 @@ export const declaration = {
|
|||
* Blocks.
|
||||
*/
|
||||
|
||||
* Program(node) {
|
||||
* Program(node: t.Program): E {
|
||||
yield node.body;
|
||||
},
|
||||
|
||||
* BlockStatement(node) {
|
||||
* BlockStatement(node: t.BlockStatement): E {
|
||||
yield enter();
|
||||
yield node.body;
|
||||
yield exit();
|
||||
|
@ -277,7 +281,7 @@ export const declaration = {
|
|||
* TODO: support form "import *".
|
||||
*/
|
||||
|
||||
* ImportDeclaration(node) {
|
||||
* ImportDeclaration(node: t.ImportDeclaration): E {
|
||||
const specifiers = yield node.specifiers;
|
||||
const path = yield node.source;
|
||||
|
||||
|
@ -288,21 +292,21 @@ export const declaration = {
|
|||
}
|
||||
},
|
||||
|
||||
* ImportDefaultSpecifier(node) {
|
||||
* ImportDefaultSpecifier(node: t.ImportDefaultSpecifier): E {
|
||||
return {
|
||||
local: yield node.local,
|
||||
imported: null,
|
||||
};
|
||||
},
|
||||
|
||||
* ImportSpecifier(node) {
|
||||
* ImportSpecifier(node: t.ImportSpecifier): E {
|
||||
return {
|
||||
local: yield node.local,
|
||||
imported: yield node.imported,
|
||||
};
|
||||
},
|
||||
|
||||
* VariableDeclarator(node) {
|
||||
* VariableDeclarator(node: t.VariableDeclarator): E {
|
||||
const path = extractRequire(node.init);
|
||||
|
||||
if (!path) {
|
||||
|
@ -325,11 +329,11 @@ export const declaration = {
|
|||
}
|
||||
},
|
||||
|
||||
* ObjectPattern(node) {
|
||||
* ObjectPattern(node: t.ObjectPattern): E {
|
||||
return yield node.properties;
|
||||
},
|
||||
|
||||
* ObjectProperty(node) {
|
||||
* ObjectProperty(node: t.ObjectProperty): E {
|
||||
const key = yield node.key;
|
||||
|
||||
// TODO: different roots.
|
||||
|
@ -337,7 +341,7 @@ export const declaration = {
|
|||
return null;
|
||||
}
|
||||
|
||||
//assert.equal(node.value.type, 'Identifier');
|
||||
//invariant(node.value.type === 'Identifier');
|
||||
|
||||
const value = yield node.value;
|
||||
|
||||
|
@ -354,7 +358,7 @@ export const declaration = {
|
|||
* TODO: support commonjs.
|
||||
*/
|
||||
|
||||
* ExportDefaultDeclaration(node) {
|
||||
* ExportDefaultDeclaration(node: t.ExportDefaultDeclaration): E {
|
||||
const reference = yield node.declaration;
|
||||
|
||||
if (reference) {
|
||||
|
@ -362,7 +366,7 @@ export const declaration = {
|
|||
}
|
||||
},
|
||||
|
||||
* ExportNamedDeclaration(node) {
|
||||
* ExportNamedDeclaration(node: t.ExportNamedDeclaration): E {
|
||||
if (!node.declaration) {
|
||||
yield node.specifiers;
|
||||
return;
|
||||
|
@ -375,7 +379,7 @@ export const declaration = {
|
|||
}
|
||||
},
|
||||
|
||||
* ExportSpecifier(node) {
|
||||
* ExportSpecifier(node: t.ExportSpecifier): E {
|
||||
const reference = yield node.local;
|
||||
let name = yield node.exported;
|
||||
|
||||
|
@ -390,7 +394,7 @@ export const declaration = {
|
|||
* Declarations.
|
||||
*/
|
||||
|
||||
* TypeAlias(node) {
|
||||
* TypeAlias(node: t.TypeAlias): E {
|
||||
const name = yield node.id;
|
||||
const params = node.typeParameters && (yield node.typeParameters);
|
||||
|
||||
|
@ -399,7 +403,7 @@ export const declaration = {
|
|||
return name;
|
||||
},
|
||||
|
||||
* InterfaceDeclaration(node) {
|
||||
* InterfaceDeclaration(node: t.InterfaceDeclaration): E {
|
||||
const name = yield node.id;
|
||||
const params = node.typeParameters && (yield node.typeParameters);
|
||||
|
||||
|
@ -408,24 +412,24 @@ export const declaration = {
|
|||
return name;
|
||||
},
|
||||
|
||||
* ClassDeclaration(node) {
|
||||
* ClassDeclaration(node: t.ClassDeclaration): E {
|
||||
const name = yield node.id;
|
||||
const params = node.typeParameters && (yield node.typeParameters);
|
||||
|
||||
// TODO: do it only for "all"-mode.
|
||||
const body = node.body;
|
||||
yield body.body.filter(is('ClassMethod'));
|
||||
yield body.body.filter(n => t.isClassMethod(n));
|
||||
|
||||
yield declare(name, node, params);
|
||||
|
||||
return name;
|
||||
},
|
||||
|
||||
* TypeParameterDeclaration(node) {
|
||||
* TypeParameterDeclaration(node: t.TypeParameterDeclaration): E {
|
||||
return yield node.params;
|
||||
},
|
||||
|
||||
* TypeParameter(node) {
|
||||
* TypeParameter(node: t.TypeParameter): E {
|
||||
return {
|
||||
name: node.name,
|
||||
default: node.default ? yield node.default : null,
|
||||
|
@ -436,16 +440,16 @@ export const declaration = {
|
|||
* Utility.
|
||||
*/
|
||||
|
||||
* StringLiteral(node) {
|
||||
* StringLiteral(node: t.StringLiteral): E {
|
||||
return node.value;
|
||||
},
|
||||
|
||||
* Identifier(node) {
|
||||
* Identifier(node: t.Identifier): E {
|
||||
return node.name;
|
||||
},
|
||||
};
|
||||
|
||||
function* extractLastPragma(comments) {
|
||||
function* extractLastPragma(comments: t.Comment[]): ?string {
|
||||
const pragmas = (yield comments).filter(Boolean);
|
||||
|
||||
return pragmas.length > 0 ? pragmas[pragmas.length - 1] : null;
|
||||
|
@ -481,6 +485,7 @@ function* extractProperty(prop, value) {
|
|||
function extractRequire(node) {
|
||||
// XXX: refactor it!
|
||||
|
||||
// TODO: use `t.*` helpers.
|
||||
const ok = node &&
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.type === 'Identifier' &&
|
||||
|
@ -493,7 +498,7 @@ function extractRequire(node) {
|
|||
const argument = node.arguments[0];
|
||||
|
||||
// TODO: warning about dynamic imports.
|
||||
assert.equal(argument.type, 'StringLiteral');
|
||||
invariant(t.isStringLiteral(argument));
|
||||
|
||||
return argument.value;
|
||||
}
|
||||
|
@ -531,7 +536,7 @@ function extractPragma(text) {
|
|||
|
||||
const pair = parsePragma(pragma);
|
||||
|
||||
assert.ok(pair);
|
||||
invariant(pair);
|
||||
|
||||
const [type, arg] = pair;
|
||||
|
||||
|
@ -570,12 +575,12 @@ function unwrapEnumSymbol(node) {
|
|||
}
|
||||
|
||||
function makeFullname(schema) {
|
||||
assert.ok(schema.namespace);
|
||||
invariant(schema.namespace);
|
||||
|
||||
return `${schema.namespace}.${schema.name}`;
|
||||
}
|
||||
|
||||
function mergeSchemas(schemas) {
|
||||
function mergeSchemas(schemas: Schemas): Schema {
|
||||
const map = new Map;
|
||||
|
||||
// TODO: overriding?
|
||||
|
@ -584,7 +589,7 @@ function mergeSchemas(schemas) {
|
|||
|
||||
for (const schema of schemas) {
|
||||
// TODO: enums?
|
||||
assert.equal(schema.type, 'record');
|
||||
invariant(schema.type === 'record');
|
||||
|
||||
for (const field of schema.fields) {
|
||||
const stored = map.get(field.name);
|
||||
|
@ -592,7 +597,7 @@ function mergeSchemas(schemas) {
|
|||
if (stored) {
|
||||
// TODO: what about enums?
|
||||
// TODO: improve checking.
|
||||
assert.equal(stored.type, field.type);
|
||||
invariant(stored.type === field.type);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -608,7 +613,3 @@ function mergeSchemas(schemas) {
|
|||
fields: Array.from(map.values()),
|
||||
};
|
||||
}
|
||||
|
||||
function is(type) {
|
||||
return node => Boolean(node) && node.type === type;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import Collector from './collector';
|
|||
// @see babel#6805.
|
||||
//export {Parser, Collector};
|
||||
|
||||
export default function collect(path) {
|
||||
export default function collect(path: string): Collector {
|
||||
const parser = new Parser;
|
||||
const collector = new Collector(parser);
|
||||
|
||||
|
|
51
src/list.js
51
src/list.js
|
@ -1,46 +1,51 @@
|
|||
import * as assert from 'assert';
|
||||
import {invariant} from './utils';
|
||||
|
||||
export default class CircularList<T: Object> {
|
||||
_mark: Symbol;
|
||||
_prev: ?T;
|
||||
_walk: ?T;
|
||||
|
||||
export default class CircularList {
|
||||
constructor() {
|
||||
this.mark = Symbol();
|
||||
this.prev = null;
|
||||
this.walk = null;
|
||||
this._mark = Symbol();
|
||||
this._prev = null;
|
||||
this._walk = null;
|
||||
}
|
||||
|
||||
get isEmpty() {
|
||||
return !this.walk;
|
||||
get isEmpty(): boolean {
|
||||
return !this._walk;
|
||||
}
|
||||
|
||||
add(entry) {
|
||||
assert.ok(!entry[this.mark]);
|
||||
add(entry: T) {
|
||||
invariant(!entry[this._mark]);
|
||||
|
||||
if (this.prev) {
|
||||
assert.ok(this.walk);
|
||||
if (this._prev) {
|
||||
invariant(this._walk);
|
||||
|
||||
this.prev = this.prev[this.mark] = entry;
|
||||
this._prev = this._prev[this._mark] = entry;
|
||||
} else {
|
||||
assert.ok(!this.walk);
|
||||
invariant(!this._walk);
|
||||
|
||||
this.walk = this.prev = entry;
|
||||
this._walk = this._prev = entry;
|
||||
}
|
||||
|
||||
entry[this.mark] = this.walk;
|
||||
entry[this._mark] = this._walk;
|
||||
|
||||
assert.ok(!this.prev || this.prev[this.mark] === this.walk);
|
||||
invariant(!this._prev || this._prev[this._mark] === this._walk);
|
||||
}
|
||||
|
||||
remove() {
|
||||
assert.ok(this.walk);
|
||||
remove(): T {
|
||||
invariant(this._walk);
|
||||
|
||||
const removed = this.walk;
|
||||
const removed = this._walk;
|
||||
|
||||
if (removed === this.prev) {
|
||||
this.walk = this.prev = null;
|
||||
if (removed === this._prev) {
|
||||
this._walk = this._prev = null;
|
||||
} else {
|
||||
this.walk = this.prev[this.mark] = removed[this.mark];
|
||||
invariant(this._prev);
|
||||
this._walk = this._prev[this._mark] = removed[this._mark];
|
||||
}
|
||||
|
||||
removed[this.mark] = null;
|
||||
removed[this._mark] = null;
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
|
|
@ -1,23 +1,32 @@
|
|||
import * as pathlib from 'path';
|
||||
import * as resolve from 'resolve';
|
||||
|
||||
import type Scope from './scope';
|
||||
import type {Schema} from './schema';
|
||||
import type {Query} from './query';
|
||||
|
||||
export default class Module {
|
||||
constructor(path, namespace) {
|
||||
+path: string;
|
||||
+namespace: string;
|
||||
_scopeCount: number;
|
||||
_exports: Map<?string, [Scope, string]>;
|
||||
|
||||
constructor(path: string, namespace: string) {
|
||||
this.path = path;
|
||||
this.namespace = namespace;
|
||||
this.scopeCount = 0;
|
||||
this._scopeCount = 0;
|
||||
this._exports = new Map;
|
||||
}
|
||||
|
||||
generateScopeId() {
|
||||
return this.scopeCount++;
|
||||
generateScopeId(): number {
|
||||
return this._scopeCount++;
|
||||
}
|
||||
|
||||
addExport(name, scope, reference) {
|
||||
addExport(name: ?string, scope: Scope, reference: string) {
|
||||
this._exports.set(name, [scope, reference]);
|
||||
}
|
||||
|
||||
query(name, params) {
|
||||
query(name: ?string, params: Schema[]): Query {
|
||||
const result = this._exports.get(name);
|
||||
|
||||
if (!result) {
|
||||
|
@ -31,14 +40,14 @@ export default class Module {
|
|||
return scope.query(reference, params);
|
||||
}
|
||||
|
||||
resolve(path) {
|
||||
resolve(path: string): string {
|
||||
const basedir = pathlib.dirname(this.path);
|
||||
|
||||
// TODO: follow symlinks.
|
||||
return resolve.sync(path, {basedir});
|
||||
}
|
||||
|
||||
exports() {
|
||||
exports(): Iterator<[Scope, string]> {
|
||||
return this._exports.values();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import * as babylon from 'babylon';
|
||||
import type {File} from '@babel/types';
|
||||
|
||||
export default class Parser {
|
||||
parse(code) {
|
||||
parse(code: string): File {
|
||||
// This parse configuration is intended to be as permissive as possible.
|
||||
return babylon.parse(code, {
|
||||
allowImportExportEverywhere: true,
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import type {Node} from '@babel/types';
|
||||
|
||||
import type Scope from './scope';
|
||||
import type {Schema} from './schema';
|
||||
|
||||
export type Query =
|
||||
| Unknown
|
||||
| Declaration
|
||||
| Template
|
||||
| Definition
|
||||
| External;
|
||||
|
||||
export type Unknown = {
|
||||
type: 'unknown',
|
||||
};
|
||||
|
||||
export type Declaration = {
|
||||
type: 'declaration',
|
||||
name: string,
|
||||
node: Node,
|
||||
scope: Scope,
|
||||
};
|
||||
|
||||
export type Template = {
|
||||
type: 'template',
|
||||
name: string,
|
||||
params: TemplateParam[],
|
||||
instances: Instance[],
|
||||
node: Node,
|
||||
scope: Scope,
|
||||
};
|
||||
|
||||
export type Definition = {
|
||||
type: 'definition',
|
||||
schema: Schema,
|
||||
scope: Scope,
|
||||
};
|
||||
|
||||
export type External = {
|
||||
type: 'external',
|
||||
info: ExternalInfo,
|
||||
scope: Scope,
|
||||
};
|
||||
|
||||
export type TemplateParam = {
|
||||
name: string,
|
||||
default: Schema,
|
||||
};
|
||||
|
||||
export type Instance = {
|
||||
params: Schema[],
|
||||
schema: Schema,
|
||||
};
|
||||
|
||||
export type ExternalInfo = {
|
||||
local: string,
|
||||
imported: ?string,
|
||||
path: string,
|
||||
};
|
|
@ -0,0 +1,77 @@
|
|||
// @see flow#3912.
|
||||
export type Schema =
|
||||
| RecordType & Top
|
||||
| EnumType & Top
|
||||
| ArrayType & Top
|
||||
| MapType & Top
|
||||
| UnionType & Top
|
||||
| FixedType & Top
|
||||
| WrappedType & Top;
|
||||
|
||||
export type Top = {
|
||||
name: string,
|
||||
namespace?: string,
|
||||
$unwrap?: boolean,
|
||||
};
|
||||
|
||||
export type Type =
|
||||
| ComplexType
|
||||
| PrimitiveType
|
||||
| ReferenceType;
|
||||
|
||||
export type WrappedType = {type: Type};
|
||||
|
||||
export type ComplexType =
|
||||
| RecordType
|
||||
| EnumType
|
||||
| ArrayType
|
||||
| MapType
|
||||
| UnionType
|
||||
| FixedType
|
||||
| WrappedType;
|
||||
|
||||
export type PrimitiveType =
|
||||
| 'null'
|
||||
| 'boolean'
|
||||
| 'int'
|
||||
| 'long'
|
||||
| 'float'
|
||||
| 'double'
|
||||
| 'bytes'
|
||||
| 'string';
|
||||
|
||||
export type ReferenceType = string;
|
||||
|
||||
export type RecordType = {
|
||||
type: 'record',
|
||||
name: string,
|
||||
fields: FieldType[],
|
||||
};
|
||||
|
||||
export type FieldType = {
|
||||
name: string,
|
||||
type: Type,
|
||||
};
|
||||
|
||||
export type EnumType = {
|
||||
type: 'enum',
|
||||
name: string,
|
||||
symbols: string[],
|
||||
};
|
||||
|
||||
export type ArrayType = {
|
||||
type: 'array',
|
||||
items: Type,
|
||||
};
|
||||
|
||||
export type MapType = {
|
||||
type: 'map',
|
||||
values: Type,
|
||||
};
|
||||
|
||||
export type UnionType = Type[];
|
||||
|
||||
export type FixedType = {
|
||||
type: 'fixed',
|
||||
size: number,
|
||||
};
|
94
src/scope.js
94
src/scope.js
|
@ -1,7 +1,17 @@
|
|||
import * as assert from 'assert';
|
||||
import type {Node} from '@babel/types';
|
||||
|
||||
import {invariant} from './utils';
|
||||
import type Module from './module';
|
||||
import type {Schema} from './schema';
|
||||
import type {Query, Template, TemplateParam, ExternalInfo} from './query';
|
||||
|
||||
export default class Scope {
|
||||
static global(schemas) {
|
||||
+parent: ?Scope;
|
||||
+module: ?Module;
|
||||
+scopeId: ?number;
|
||||
_entries: Map<string, Query>;
|
||||
|
||||
static global(schemas: Schema[]) {
|
||||
const global = new Scope(null, null);
|
||||
|
||||
for (const schema of schemas) {
|
||||
|
@ -11,104 +21,102 @@ export default class Scope {
|
|||
return global;
|
||||
}
|
||||
|
||||
constructor(parent, module) {
|
||||
constructor(parent: ?Scope, module: ?Module) {
|
||||
this.parent = parent;
|
||||
this.module = module;
|
||||
this.scopeId = module && module.generateScopeId();
|
||||
this.entries = new Map;
|
||||
this._entries = new Map;
|
||||
}
|
||||
|
||||
get namespace() {
|
||||
assert.ok(this.module);
|
||||
get namespace(): string {
|
||||
invariant(this.module);
|
||||
|
||||
let namespace = this.module.namespace;
|
||||
|
||||
// Nested scopes.
|
||||
if (this.scopeId > 0) {
|
||||
if (this.scopeId != null && this.scopeId > 0) {
|
||||
namespace += '._' + this.scopeId;
|
||||
}
|
||||
|
||||
return namespace;
|
||||
}
|
||||
|
||||
extend(module = null) {
|
||||
extend(module: ?Module = null): Scope {
|
||||
return new Scope(this, module || this.module);
|
||||
}
|
||||
|
||||
addDeclaration(name, node, params) {
|
||||
assert.ok(!this.entries.has(name));
|
||||
addDeclaration(name: string, node: Node, params: TemplateParam[]) {
|
||||
invariant(!this._entries.has(name));
|
||||
|
||||
const isTemplate = Boolean(params);
|
||||
|
||||
const entry = {
|
||||
type: isTemplate ? 'template' : 'declaration',
|
||||
const entry = params.length > 0 ? {
|
||||
type: 'template',
|
||||
name,
|
||||
params,
|
||||
instances: [],
|
||||
node,
|
||||
scope: this,
|
||||
} : {
|
||||
type: 'declaration',
|
||||
name,
|
||||
node,
|
||||
scope: this,
|
||||
};
|
||||
|
||||
if (isTemplate) {
|
||||
entry.params = params;
|
||||
entry.instances = [];
|
||||
this._entries.set(name, entry);
|
||||
}
|
||||
|
||||
this.entries.set(name, entry);
|
||||
}
|
||||
addInstance(name: string, schema: Schema, params: Schema[]) {
|
||||
const template = this._entries.get(name);
|
||||
|
||||
addInstance(name, schema, params) {
|
||||
const template = this.entries.get(name);
|
||||
|
||||
assert.ok(template);
|
||||
assert.equal(template.type, 'template');
|
||||
invariant(template);
|
||||
invariant(template.type === 'template');
|
||||
|
||||
template.instances.push({params, schema});
|
||||
}
|
||||
|
||||
addDefinition(schema, declared) {
|
||||
const decl = this.entries.get(schema.name);
|
||||
addDefinition(schema: Schema, declared: boolean) {
|
||||
const decl = this._entries.get(schema.name);
|
||||
|
||||
if (declared) {
|
||||
assert.ok(decl);
|
||||
assert.equal(decl.type, 'declaration');
|
||||
invariant(decl);
|
||||
invariant(decl.type === 'declaration');
|
||||
} else {
|
||||
assert.ok(!decl);
|
||||
invariant(!decl);
|
||||
}
|
||||
|
||||
this.entries.set(schema.name, {
|
||||
this._entries.set(schema.name, {
|
||||
type: 'definition',
|
||||
schema,
|
||||
scope: this,
|
||||
});
|
||||
}
|
||||
|
||||
addImport(info) {
|
||||
assert.ok(!this.entries.has(info.local));
|
||||
addImport(info: ExternalInfo) {
|
||||
invariant(!this._entries.has(info.local));
|
||||
|
||||
this.entries.set(info.local, {
|
||||
this._entries.set(info.local, {
|
||||
type: 'external',
|
||||
info,
|
||||
scope: this,
|
||||
});
|
||||
}
|
||||
|
||||
addExport(name, reference) {
|
||||
assert.ok(this.module);
|
||||
addExport(name: string, reference: string) {
|
||||
invariant(this.module);
|
||||
|
||||
this.module.addExport(name, this, reference);
|
||||
}
|
||||
|
||||
resolve(path) {
|
||||
assert.ok(this.module);
|
||||
resolve(path: string): string {
|
||||
invariant(this.module);
|
||||
|
||||
return this.module.resolve(path);
|
||||
}
|
||||
|
||||
query(name, params) {
|
||||
const entry = this.entries.get(name);
|
||||
query(name: string, params: Schema[]): Query {
|
||||
const entry = this._entries.get(name);
|
||||
|
||||
if (entry && entry.type === 'template') {
|
||||
assert.ok(params);
|
||||
|
||||
const augmented = entry.params.map((p, i) => params[i] || p.default);
|
||||
const schema = findInstance(entry, augmented);
|
||||
|
||||
|
@ -126,7 +134,7 @@ export default class Scope {
|
|||
}
|
||||
|
||||
if (this.parent) {
|
||||
return this.parent.query(name);
|
||||
return this.parent.query(name, params);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -135,7 +143,7 @@ export default class Scope {
|
|||
}
|
||||
}
|
||||
|
||||
function findInstance(template, queried) {
|
||||
function findInstance(template: Template, queried: Schema[]): ?Schema {
|
||||
for (const {schema, params} of template.instances) {
|
||||
// TODO: compare complex structures.
|
||||
const same = params.every((p, i) => p === queried[i]);
|
||||
|
|
11
src/utils.js
11
src/utils.js
|
@ -1,4 +1,6 @@
|
|||
export function partition(iter, predicate) {
|
||||
import * as assert from 'assert';
|
||||
|
||||
export function partition<T>(iter: Iterable<T>, predicate: T => boolean): [T[], T[]] {
|
||||
const left = [];
|
||||
const right = [];
|
||||
|
||||
|
@ -9,6 +11,11 @@ export function partition(iter, predicate) {
|
|||
return [left, right];
|
||||
}
|
||||
|
||||
export function isNode(it) {
|
||||
// TODO: avoid it?
|
||||
export function isNode(it: any): boolean %checks {
|
||||
return it && typeof it === 'object' && it.type;
|
||||
}
|
||||
|
||||
// I so much dream about the user guards...
|
||||
// @see flow#112.
|
||||
export const invariant = assert.ok;
|
||||
|
|
Loading…
Reference in New Issue