Skip to content

Commit

Permalink
Merge f705803 into 5f9ae80
Browse files Browse the repository at this point in the history
  • Loading branch information
teppeis committed Jun 4, 2019
2 parents 5f9ae80 + f705803 commit c03a6a7
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 9 deletions.
45 changes: 40 additions & 5 deletions lib/parser.js
Expand Up @@ -3,6 +3,7 @@
const parser = require('@babel/parser');
const doctrine = require('doctrine');
const difference = require('lodash.difference');
const flat = require('array.prototype.flat');
const estraverse = require('estraverse-fb');
const {traverse} = estraverse;
const {Syntax} = estraverse;
Expand Down Expand Up @@ -206,11 +207,10 @@ Parser.prototype.extractToRequireTypeFromJsDoc_ = function(comments) {
.reduce((prev, comment) => {
const jsdoc = doctrine.parse(`/* ${comment.value}*/`, {unwrap: true});
jsdoc.tags
.filter(
tag => tagsHavingType.has(tag.title) && tag.type && tag.type.type === 'NameExpression'
)
.forEach(tag => {
prev.push(tag.type.name);
.filter(tag => tagsHavingType.has(tag.title) && tag.type)
.map(tag => this.extractType(tag.type))
.forEach(names => {
prev.push(...names);
});
return prev;
}, [])
Expand All @@ -219,6 +219,41 @@ Parser.prototype.extractToRequireTypeFromJsDoc_ = function(comments) {
.reduce(this.uniq_, []);
};

Parser.prototype.extractType = function(type) {
let result;
switch (type.type) {
case 'NameExpression':
return [type.name];
case 'NullableType':
case 'NonNullableType':
case 'OptionalType':
case 'RestType':
return this.extractType(type.expression);
case 'TypeApplication':
result = this.extractType(type.expression);
result.push(...flat(type.applications.map(app => this.extractType(app))));
break;
case 'UnionType':
return flat(type.elements.map(el => this.extractType(el)));
case 'RecordType':
return flat(type.fields.map(field => this.extractType(field)));
case 'FieldType':
return this.extractType(type.value);
case 'FunctionType':
result = flat(type.params.map(param => this.extractType(param)));
if (type.result) {
result.push(...this.extractType(type.result));
}
if (type.this) {
result.push(...this.extractType(type.this));
}
break;
default:
result = [];
}
return result;
};

const tagsHavingType = new Set([
'const',
'define',
Expand Down
173 changes: 169 additions & 4 deletions test/parse.js
Expand Up @@ -2,31 +2,196 @@

require('chai').should();
const fs = require('fs');
const asserts = require('./lib/asserts');
const {assertFile} = require('./lib/asserts');
const assert = require('assert').strict;
const {Parser} = require('../');

describe('Parser', () => {
context('ES5', () => {
const files = fs.readdirSync(`${__dirname}/fixtures/parse/`);
files.forEach(file => it(file, () => asserts.assertFile(`/parse/${file}`)));
files.forEach(file => it(file, () => assertFile(`/parse/${file}`)));
});

context('ES6 script', () => {
const files = fs.readdirSync(`${__dirname}/fixtures/parse-es6-script/`);
files.forEach(file =>
it(file, () => asserts.assertFile(`/parse-es6-script/${file}`, {parserOptions: {}}))
it(file, () => assertFile(`/parse-es6-script/${file}`, {parserOptions: {}}))
);
});

context('ES6 module', () => {
const files = fs.readdirSync(`${__dirname}/fixtures/parse-es6-module/`);
files.forEach(file =>
it(file, () =>
asserts.assertFile(`/parse-es6-module/${file}`, {
assertFile(`/parse-es6-module/${file}`, {
parserOptions: {
sourceType: 'module',
},
})
)
);
});
describe('extractType', () => {
let parser;
beforeEach(() => {
parser = new Parser();
});
it('NameExpression', () => {
const actual = parser.extractType({
type: 'NameExpression',
name: 'string',
});
assert.deepEqual(actual, ['string']);
});
it('NullableType', () => {
const actual = parser.extractType({
type: 'NullableType',
expression: {
type: 'NameExpression',
name: 'string',
},
prefix: true,
});
assert.deepEqual(actual, ['string']);
});
it('NonNullableType', () => {
const actual = parser.extractType({
type: 'NonNullableType',
expression: {
type: 'NameExpression',
name: 'string',
},
prefix: true,
});
assert.deepEqual(actual, ['string']);
});
it('OptionalType', () => {
const actual = parser.extractType({
type: 'OptionalType',
expression: {
type: 'NameExpression',
name: 'string',
},
});
assert.deepEqual(actual, ['string']);
});
it('TypeApplication', () => {
const actual = parser.extractType({
type: 'TypeApplication',
expression: {
type: 'NameExpression',
name: 'Foo',
},
applications: [
{
type: 'NameExpression',
name: 'Bar',
},
{
type: 'NameExpression',
name: 'Baz',
},
],
});
assert.deepEqual(actual.sort(), ['Bar', 'Baz', 'Foo']);
});
it('UnionType', () => {
const actual = parser.extractType({
type: 'UnionType',
elements: [
{
type: 'NameExpression',
name: 'string',
},
{
type: 'NameExpression',
name: 'number',
},
],
});
assert.deepEqual(actual.sort(), ['number', 'string']);
});
it('RecordType', () => {
const actual = parser.extractType({
type: 'RecordType',
fields: [
{
type: 'FieldType',
key: 'foo',
value: {
type: 'NameExpression',
name: 'string',
},
},
{
type: 'FieldType',
key: 'bar',
value: {
type: 'NameExpression',
name: 'number',
},
},
],
});
assert.deepEqual(actual.sort(), ['number', 'string']);
});
it('RestType', () => {
const actual = parser.extractType({
type: 'RestType',
expression: {
type: 'NameExpression',
name: 'string',
},
});
assert.deepEqual(actual.sort(), ['string']);
});
it('FunctionType', () => {
const actual = parser.extractType({
type: 'FunctionType',
params: [
{
type: 'NameExpression',
name: 'boolean',
},
{
type: 'RestType',
expression: {
type: 'NameExpression',
name: 'string',
},
},
],
result: {
type: 'NameExpression',
name: 'number',
},
});
assert.deepEqual(actual.sort(), ['boolean', 'number', 'string']);
});
it('FunctionType: this', () => {
const actual = parser.extractType({
type: 'FunctionType',
params: [],
result: null,
this: {
type: 'NameExpression',
name: 'Date',
},
});
assert.deepEqual(actual.sort(), ['Date']);
});
it('FunctionType: new', () => {
const actual = parser.extractType({
type: 'FunctionType',
params: [],
result: null,
this: {
type: 'NameExpression',
name: 'Date',
},
new: true,
});
assert.deepEqual(actual.sort(), ['Date']);
});
});
});

0 comments on commit c03a6a7

Please sign in to comment.