Support for multi-file projects
parent
e2abcc4f8a
commit
345b381979
|
@ -37,7 +37,7 @@ Output:
|
|||
|
||||
## TODO
|
||||
|
||||
* External types (`import` and `require`).
|
||||
* Generics.
|
||||
* Errors and warnings.
|
||||
* Support commonjs modules.
|
||||
* Documentation.
|
||||
|
|
116
lib/collector.js
116
lib/collector.js
|
@ -6,28 +6,53 @@ const pathlib = require('path');
|
|||
|
||||
const extractors = require('./extractors');
|
||||
const Command = require('./commands');
|
||||
const Scope = require('./scope');
|
||||
const {Scope, Module} = require('./scope');
|
||||
const {consume, log} = require('./utils');
|
||||
|
||||
class Collector {
|
||||
constructor(parser) {
|
||||
constructor(parser, root = '.') {
|
||||
this.root = root;
|
||||
this.parser = parser;
|
||||
this.schemas = [];
|
||||
this.waiters = Object.create(null);
|
||||
this.tasks = [];
|
||||
this.modules = new Map;
|
||||
this.global = Scope.global([]);
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
collect(path) {
|
||||
// TODO: follow symlinks.
|
||||
path = pathlib.resolve(path);
|
||||
|
||||
let module = this.modules.get(path);
|
||||
|
||||
if (module) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: error wrapping.
|
||||
const code = fs.readFileSync(path, 'utf8');
|
||||
const ast = this.parser.parse(code);
|
||||
|
||||
const global = Scope.global([]);
|
||||
|
||||
// TODO: customize it.
|
||||
const namespace = pathToNamespace(path);
|
||||
const namespace = pathToNamespace(pathlib.relative('.', path));
|
||||
|
||||
const scope = global.extend(namespace);
|
||||
module = new Module(path, namespace);
|
||||
|
||||
const scope = this.global.extend(module);
|
||||
|
||||
this._spawn(ast.program, scope);
|
||||
|
||||
this.modules.set(path, module);
|
||||
|
||||
if (!this.running) {
|
||||
try {
|
||||
this.running = true;
|
||||
this._schedule();
|
||||
} finally {
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Given the AST output of babylon parse, walk through in a depth-first order.
|
||||
|
@ -57,7 +82,7 @@ class Collector {
|
|||
if (type && type in extractors) {
|
||||
const task = this._collect(node, scope);
|
||||
|
||||
this._schedule(task);
|
||||
this.tasks.push(task);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -97,24 +122,21 @@ class Collector {
|
|||
this._spawn(value.data, scope);
|
||||
|
||||
break;
|
||||
case 'provide':
|
||||
const schema = value.data;
|
||||
case 'define':
|
||||
scope.addSchema(value.data);
|
||||
this.schemas.push(value.data);
|
||||
|
||||
scope.addSchema(schema);
|
||||
this.schemas.push(schema);
|
||||
this._wakeup(schema.name);
|
||||
break;
|
||||
case 'external':
|
||||
scope.addImport(value.data);
|
||||
|
||||
break;
|
||||
case 'provide':
|
||||
scope.addExport(value.data);
|
||||
|
||||
break;
|
||||
case 'query':
|
||||
let info;
|
||||
|
||||
while (!(info = scope.query(value.data))) {
|
||||
yield value.data;
|
||||
}
|
||||
|
||||
// TODO: support externals.
|
||||
assert(info.type, 'local');
|
||||
result = info.schema;
|
||||
result = yield* this._query(scope, value.data);
|
||||
|
||||
break;
|
||||
case 'enter':
|
||||
|
@ -146,28 +168,50 @@ class Collector {
|
|||
}
|
||||
}
|
||||
|
||||
_schedule(task) {
|
||||
const {done, value} = task.next();
|
||||
* _query(scope, name) {
|
||||
let result;
|
||||
|
||||
if (done) {
|
||||
return;
|
||||
// Wait any information about the reference.
|
||||
while ((result = scope.query(name)).type === 'unknown') {
|
||||
yield;
|
||||
}
|
||||
|
||||
const waiters = this.waiters[value] = this.waiters[value] || [];
|
||||
if (result.type === 'local') {
|
||||
return result.schema;
|
||||
}
|
||||
|
||||
waiters.push(task);
|
||||
assert.equal(result.type, 'external');
|
||||
|
||||
// TODO: reexports.
|
||||
|
||||
const modulePath = scope.resolve(result.info.path);
|
||||
|
||||
this.collect(modulePath);
|
||||
|
||||
const module = this.modules.get(modulePath);
|
||||
const {imported} = result.info;
|
||||
let schema;
|
||||
|
||||
while (!(schema = module.query(imported))) {
|
||||
yield;
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
_wakeup(name) {
|
||||
if (!this.waiters[name]) {
|
||||
return;
|
||||
}
|
||||
_schedule() {
|
||||
// TODO: prevent infinite loop.
|
||||
|
||||
const tasks = this.waiters[name];
|
||||
delete this.waiters[name];
|
||||
const {tasks} = this;
|
||||
|
||||
for (const task of tasks) {
|
||||
this._schedule(task);
|
||||
for (let i = 0; tasks.length > 0; i = ++i % tasks.length) {
|
||||
const {done} = tasks[i].next();
|
||||
|
||||
if (done) {
|
||||
// TODO: use linked list instead.
|
||||
tasks.splice(i, 1);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,16 @@ class Command {
|
|||
return new Command('spawn', node);
|
||||
}
|
||||
|
||||
static provide(schema) {
|
||||
return new Command('provide', schema);
|
||||
static define(schema) {
|
||||
return new Command('define', schema);
|
||||
}
|
||||
|
||||
static external(external) {
|
||||
return new Command('external', external);
|
||||
}
|
||||
|
||||
static provide(internal) {
|
||||
return new Command('provide', internal);
|
||||
}
|
||||
|
||||
static query(name) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const assert = require('assert');
|
||||
|
||||
const {spawn, provide, query, enter, exit, namespace} = require('./commands');
|
||||
const {spawn, define, external, provide, query, enter, exit, namespace} = require('./commands');
|
||||
const {partition} = require('./utils');
|
||||
|
||||
const extractors = {
|
||||
|
@ -16,7 +16,9 @@ const extractors = {
|
|||
schema.name = yield node.id;
|
||||
schema.namespace = yield namespace();
|
||||
|
||||
yield provide(schema);
|
||||
yield define(schema);
|
||||
|
||||
return schema;
|
||||
},
|
||||
|
||||
* InterfaceDeclaration(node) {
|
||||
|
@ -40,7 +42,9 @@ const extractors = {
|
|||
schema.name = yield node.id;
|
||||
schema.namespace = yield namespace();
|
||||
|
||||
yield provide(schema);
|
||||
yield define(schema);
|
||||
|
||||
return schema;
|
||||
},
|
||||
|
||||
* ClassDeclaration(node) {
|
||||
|
@ -56,7 +60,9 @@ const extractors = {
|
|||
schema.name = yield node.id;
|
||||
schema.namespace = yield namespace();
|
||||
|
||||
yield provide(schema);
|
||||
yield define(schema);
|
||||
|
||||
return schema;
|
||||
},
|
||||
|
||||
* ClassBody(node) {
|
||||
|
@ -75,6 +81,7 @@ const extractors = {
|
|||
},
|
||||
|
||||
* ClassMethod(node) {
|
||||
// For cases, when the body has references to the class.
|
||||
yield spawn(node.body);
|
||||
|
||||
return null;
|
||||
|
@ -230,6 +237,144 @@ const extractors = {
|
|||
* CommentBlock(node) {
|
||||
return extractPragma(node.value);
|
||||
},
|
||||
|
||||
/*
|
||||
* Imports.
|
||||
*
|
||||
* TODO: warning about "import typeof".
|
||||
* TODO: support form "import *".
|
||||
*/
|
||||
|
||||
* ImportDeclaration(node) {
|
||||
const specifiers = yield node.specifiers;
|
||||
const path = yield node.source;
|
||||
|
||||
for (const specifier of specifiers) {
|
||||
specifier.path = path;
|
||||
|
||||
yield external(specifier);
|
||||
}
|
||||
},
|
||||
|
||||
* ImportDefaultSpecifier(node) {
|
||||
return {
|
||||
local: yield node.local,
|
||||
imported: null,
|
||||
};
|
||||
},
|
||||
|
||||
* ImportSpecifier(node) {
|
||||
return {
|
||||
local: yield node.local,
|
||||
imported: yield node.imported,
|
||||
};
|
||||
},
|
||||
|
||||
* VariableDeclarator(node) {
|
||||
const path = extractRequire(node.init);
|
||||
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let specifiers = yield node.id;
|
||||
|
||||
if (typeof specifiers === 'string') {
|
||||
specifiers = [{
|
||||
local: specifiers,
|
||||
imported: null,
|
||||
}];
|
||||
}
|
||||
|
||||
for (const specifier of specifiers) {
|
||||
specifier.path = path;
|
||||
|
||||
yield external(specifier);
|
||||
}
|
||||
},
|
||||
|
||||
* ObjectPattern(node) {
|
||||
return yield node.properties;
|
||||
},
|
||||
|
||||
* ObjectProperty(node) {
|
||||
const key = yield node.key;
|
||||
|
||||
// TODO: different roots.
|
||||
if (node.value.type !== 'Identifier') {
|
||||
return null;
|
||||
}
|
||||
|
||||
//assert.equal(node.value.type, 'Identifier');
|
||||
|
||||
const value = yield node.value;
|
||||
|
||||
return {
|
||||
local: value,
|
||||
imported: key,
|
||||
};
|
||||
},
|
||||
|
||||
* StringLiteral(node) {
|
||||
return node.value;
|
||||
},
|
||||
|
||||
/*
|
||||
* Exports.
|
||||
*
|
||||
* TODO: support "export from" form.
|
||||
* TODO: support commonjs.
|
||||
*/
|
||||
|
||||
* ExportDefaultDeclaration(node) {
|
||||
let schema = yield node.declaration;
|
||||
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof schema === 'string') {
|
||||
schema = yield query(schema);
|
||||
}
|
||||
|
||||
yield provide({
|
||||
schema,
|
||||
exported: null,
|
||||
});
|
||||
},
|
||||
* ExportNamedDeclaration(node) {
|
||||
if (!node.declaration) {
|
||||
yield node.specifiers;
|
||||
return;
|
||||
}
|
||||
|
||||
const schema = yield node.declaration;
|
||||
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield provide({
|
||||
schema,
|
||||
exported: schema.name,
|
||||
});
|
||||
},
|
||||
|
||||
* ExportSpecifier(node) {
|
||||
let exported = yield node.exported;
|
||||
|
||||
if (exported === 'default') {
|
||||
exported = null;
|
||||
}
|
||||
|
||||
const name = yield node.local;
|
||||
const schema = yield query(name);
|
||||
|
||||
yield provide({
|
||||
schema,
|
||||
exported,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function* extractLastPragma(comments) {
|
||||
|
@ -255,7 +400,7 @@ function* extractProperty(prop, value) {
|
|||
|
||||
if (type.type === 'record') {
|
||||
type.namespace = yield namespace();
|
||||
yield provide(type);
|
||||
yield define(type);
|
||||
type = type.name;
|
||||
}
|
||||
|
||||
|
@ -265,6 +410,26 @@ function* extractProperty(prop, value) {
|
|||
};
|
||||
}
|
||||
|
||||
function extractRequire(node) {
|
||||
// XXX: refactor it!
|
||||
|
||||
const ok = node &&
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'require';
|
||||
|
||||
if (!ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const argument = node.arguments[0];
|
||||
|
||||
// TODO: warning about dynamic imports.
|
||||
assert.equal(argument.type, 'StringLiteral');
|
||||
|
||||
return argument.value;
|
||||
}
|
||||
|
||||
function parsePragma(pragma) {
|
||||
let [type, arg] = pragma.split(/\s+/);
|
||||
|
||||
|
|
115
lib/scope.js
115
lib/scope.js
|
@ -1,26 +1,44 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const pathlib = require('path');
|
||||
|
||||
const resolve = require('resolve');
|
||||
|
||||
class Scope {
|
||||
static global(schemas) {
|
||||
return new Scope(null, '', schemas);
|
||||
}
|
||||
const global = new Scope(null, null);
|
||||
|
||||
constructor(parent, namespace, schemas) {
|
||||
this.parent = parent;
|
||||
this.schemas = new Map(schemas);
|
||||
this.externals = new Map;
|
||||
this.namespace = namespace;
|
||||
this.childCount = 0;
|
||||
}
|
||||
|
||||
extend(namespace = null) {
|
||||
if (!namespace) {
|
||||
namespace = `${this.namespace}._${this.childCount++}`;
|
||||
for (const schema of schemas) {
|
||||
global.addSchema(schema);
|
||||
}
|
||||
|
||||
return new Scope(this, namespace, []);
|
||||
return global;
|
||||
}
|
||||
|
||||
constructor(parent, module) {
|
||||
this.parent = parent;
|
||||
this.module = module;
|
||||
this.scopeId = module && module.generateScopeId();
|
||||
this.schemas = new Map;
|
||||
this.imports = new Map;
|
||||
}
|
||||
|
||||
get namespace() {
|
||||
assert(this.module);
|
||||
|
||||
let namespace = this.module.namespace;
|
||||
|
||||
// Nested scopes.
|
||||
if (this.scopeId > 0) {
|
||||
namespace += '._' + this.scopeId;
|
||||
}
|
||||
|
||||
return namespace;
|
||||
}
|
||||
|
||||
extend(module = null) {
|
||||
return new Scope(this, module || this.module);
|
||||
}
|
||||
|
||||
addSchema(schema) {
|
||||
|
@ -29,12 +47,26 @@ class Scope {
|
|||
this.schemas.set(schema.name, schema);
|
||||
}
|
||||
|
||||
addExternal(external) {
|
||||
this.externals.set(external.name, external);
|
||||
addImport(info) {
|
||||
assert(!this.imports.has(info.local));
|
||||
|
||||
this.imports.set(info.local, info);
|
||||
}
|
||||
|
||||
addExport(info) {
|
||||
assert(this.module);
|
||||
|
||||
this.module.addExport(info.exported, info.schema);
|
||||
}
|
||||
|
||||
resolve(path) {
|
||||
assert(this.module);
|
||||
|
||||
return this.module.resolve(path);
|
||||
}
|
||||
|
||||
query(name) {
|
||||
const result = this._querySchema(name) || this._queryExternal(name);
|
||||
const result = this._querySchema(name) || this._queryImport(name);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
|
@ -44,26 +76,59 @@ class Scope {
|
|||
return this.parent.query(name);
|
||||
}
|
||||
|
||||
return null;
|
||||
return {
|
||||
type: 'unknown',
|
||||
};
|
||||
}
|
||||
|
||||
_querySchema(name) {
|
||||
const schema = this.schemas.get(name);
|
||||
|
||||
return schema ? {
|
||||
return schema && {
|
||||
type: 'local',
|
||||
schema,
|
||||
} : null;
|
||||
};
|
||||
}
|
||||
|
||||
_queryExternal(name) {
|
||||
const info = this.externals.get(name);
|
||||
_queryImport(name) {
|
||||
const info = this.imports.get(name);
|
||||
|
||||
return info ? {
|
||||
return info && {
|
||||
type: 'external',
|
||||
info,
|
||||
} : null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Scope;
|
||||
class Module {
|
||||
constructor(path, namespace) {
|
||||
this.path = path;
|
||||
this.namespace = namespace;
|
||||
this.scopeCount = 0;
|
||||
this.exports = new Map;
|
||||
}
|
||||
|
||||
generateScopeId() {
|
||||
return this.scopeCount++;
|
||||
}
|
||||
|
||||
addExport(name, schema) {
|
||||
this.exports.set(name, schema);
|
||||
}
|
||||
|
||||
query(name) {
|
||||
return this.exports.get(name) || null;
|
||||
}
|
||||
|
||||
resolve(path) {
|
||||
const basedir = pathlib.dirname(this.path);
|
||||
|
||||
// TODO: follow symlinks.
|
||||
return resolve.sync(path, {basedir});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Scope,
|
||||
Module,
|
||||
};
|
||||
|
|
|
@ -23,10 +23,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"babylon": "^6.18.0",
|
||||
"optimist": "^0.6.1"
|
||||
"optimist": "^0.6.1",
|
||||
"resolve": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jsondiffpatch": "^0.2.5"
|
||||
"jsondiffpatch": "^0.2.5"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tests/do"
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import D, {A, B as F} from './externals/first';
|
||||
import type {C} from './externals/first';
|
||||
|
||||
import UM from './unused/module';
|
||||
|
||||
const {N, M, K: T} = require('./externals/second');
|
||||
const P = require('./externals/second');
|
||||
|
||||
const UR = require('./unused/module');
|
||||
|
||||
type X = {
|
||||
a: A,
|
||||
b: F,
|
||||
c: C,
|
||||
d: D,
|
||||
};
|
||||
|
||||
type Y = {
|
||||
n: N,
|
||||
m: M,
|
||||
k: T,
|
||||
p: P,
|
||||
};
|
||||
|
||||
// ###
|
||||
[
|
||||
{
|
||||
type: 'record',
|
||||
name: 'A',
|
||||
namespace: 'externals.first',
|
||||
fields: [{name: 'a', type: 'boolean'}],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'B',
|
||||
namespace: 'externals.first',
|
||||
fields: [{name: 'b', type: 'string'}],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'CC',
|
||||
namespace: 'externals.first',
|
||||
fields: [{name: 'c', type: 'double'}],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'D',
|
||||
namespace: 'externals.first',
|
||||
fields: [{name: 'd', type: 'double'}],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'N',
|
||||
namespace: 'externals.second',
|
||||
fields: [{name: 'n', type: 'boolean'}],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'M',
|
||||
namespace: 'externals.second',
|
||||
fields: [{name: 'm', type: 'string'}],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'KK',
|
||||
namespace: 'externals.second',
|
||||
fields: [{name: 'k', type: 'double'}],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'P',
|
||||
namespace: 'externals.second',
|
||||
fields: [{name: 'p', type: 'double'}],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'X',
|
||||
namespace: 'externals',
|
||||
fields: [
|
||||
{name: 'a', type: 'externals.first.A'},
|
||||
{name: 'b', type: 'externals.first.B'},
|
||||
{name: 'c', type: 'externals.first.CC'},
|
||||
{name: 'd', type: 'externals.first.D'},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'Y',
|
||||
namespace: 'externals',
|
||||
fields: [
|
||||
{name: 'n', type: 'externals.second.N'},
|
||||
{name: 'm', type: 'externals.second.M'},
|
||||
{name: 'k', type: 'externals.second.KK'},
|
||||
{name: 'p', type: 'externals.second.P'},
|
||||
],
|
||||
},
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
export type A = {
|
||||
a: boolean,
|
||||
};
|
||||
|
||||
class B {
|
||||
b: string;
|
||||
}
|
||||
|
||||
interface CC {
|
||||
c: number;
|
||||
}
|
||||
|
||||
class D {
|
||||
d: number;
|
||||
}
|
||||
|
||||
export {B, CC as C, D as default};
|
|
@ -0,0 +1,17 @@
|
|||
export type N = {
|
||||
n: boolean,
|
||||
};
|
||||
|
||||
class M {
|
||||
m: string;
|
||||
}
|
||||
|
||||
interface KK {
|
||||
k: number;
|
||||
}
|
||||
|
||||
class P {
|
||||
p: number;
|
||||
}
|
||||
|
||||
export {M, KK as K, P as default};
|
|
@ -59,65 +59,65 @@ type Y = {
|
|||
{
|
||||
type: 'double',
|
||||
name: 'X',
|
||||
namespace: 'scopes._0',
|
||||
namespace: 'scopes._1',
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'Y',
|
||||
namespace: 'scopes._0',
|
||||
namespace: 'scopes._1',
|
||||
fields: [{name: 'x', type: 'X'}],
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'Z',
|
||||
namespace: 'scopes._0',
|
||||
namespace: 'scopes._1',
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'Test',
|
||||
namespace: 'scopes._1',
|
||||
fields: [],
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'X',
|
||||
namespace: 'scopes._0._0',
|
||||
namespace: 'scopes._2',
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'Y',
|
||||
namespace: 'scopes._0._0',
|
||||
namespace: 'scopes._2',
|
||||
fields: [
|
||||
{name: 'x', type: 'X'},
|
||||
{name: 'z', type: 'scopes._0.Z'},
|
||||
{name: 'z', type: 'scopes._1.Z'},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'double',
|
||||
name: 'X',
|
||||
namespace: 'scopes._0._1',
|
||||
namespace: 'scopes._3',
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'Y',
|
||||
namespace: 'scopes._0._1',
|
||||
namespace: 'scopes._3',
|
||||
fields: [
|
||||
{name: 'x', type: 'X'},
|
||||
{name: 'z', type: 'scopes._0.Z'},
|
||||
{name: 'z', type: 'scopes._1.Z'},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'X',
|
||||
namespace: 'scopes._0._2',
|
||||
namespace: 'scopes._4',
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'Y',
|
||||
namespace: 'scopes._0._2',
|
||||
namespace: 'scopes._4',
|
||||
fields: [
|
||||
{name: 'x', type: 'X'},
|
||||
{name: 'z', type: 'scopes._0.Z'},
|
||||
{name: 'z', type: 'scopes._1.Z'},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'record',
|
||||
name: 'Test',
|
||||
namespace: 'scopes._0',
|
||||
fields: [],
|
||||
},
|
||||
]
|
||||
|
|
|
@ -17,7 +17,7 @@ class Test {
|
|||
{
|
||||
type: 'record',
|
||||
name: 'X',
|
||||
namespace: 'typeInMethod._0',
|
||||
namespace: 'typeInMethod._1',
|
||||
fields: [{name: 't', type: 'typeInMethod.Test'}],
|
||||
},
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue