Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit 8b27145

Browse files
committedDec 22, 2015
feat: Support es6 class syntax
1 parent f26cf79 commit 8b27145

10 files changed

+157
-36
lines changed
 

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Create typescript definitions files (d.ts) from react components.
1111

1212
# Features
1313

14+
* ES6 and ES7 class syntax
1415
* Most PropTypes
1516
* any, array, bool, func, number, object, string, node, element, oneOfType, arrayOf
1617
* required PropTypes

‎index.ts

+43-13
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export function generateFromSource(name: string, code: string, options: IOptions
8383
const defaultInstanceOfResolver: InstanceOfResolver = (name: string): string => undefined;
8484

8585
export function generateFromAst(name: string, ast: any, options: IOptions = {}): string {
86-
const {classname, propTypes}: IParsingResult = parseAst(ast, options.instanceOfResolver);
86+
const {exportType, classname, propTypes}: IParsingResult = parseAst(ast, options.instanceOfResolver);
8787
const writer: Writer = options.writer || new Writer();
8888
writer.declareModule(name, () => {
8989
writer.import('* as React', 'react');
@@ -98,46 +98,73 @@ export function generateFromAst(name: string, ast: any, options: IOptions = {}):
9898
writer.nl();
9999
writer.props(classname, propTypes);
100100
writer.nl();
101-
writer.exportDefault(() => {
101+
writer.exportDeclaration(exportType, () => {
102102
writer.class(classname, !!propTypes);
103103
});
104104
});
105105
return writer.toString();
106106
}
107107

108+
enum ExportType {
109+
default,
110+
named
111+
}
112+
108113
interface IParsingResult {
114+
exportType: ExportType;
109115
classname: string;
110116
propTypes: IPropTypes;
111117
}
112118

113119
function parseAst(ast: any, instanceOfResolver: InstanceOfResolver): IParsingResult {
120+
let exportType: ExportType;
114121
let classname: string;
115-
let propTypes: IPropTypes = undefined;
122+
let propTypes: IPropTypes = {};
116123
walk(ast.program, {
124+
'ExportNamedDeclaration': (exportNode: any): void => {
125+
exportType = ExportType.named;
126+
},
127+
'ExportDefaultDeclaration': (exportNode: any): void => {
128+
exportType = ExportType.default;
129+
},
117130
'ClassDeclaration': (classNode: any): void => {
118131
classname = classNode.id.name;
119132
walk(classNode.body, {
120133
'ClassProperty': (attributeNode: any): void => {
121134
if (attributeNode.key.name == 'propTypes') {
122-
propTypes = {};
123-
walk(attributeNode.value, {
124-
'ObjectProperty': (propertyNode: any): void => {
125-
const prop: IProp = getTypeFromPropType(propertyNode.value, instanceOfResolver);
126-
prop.documentation = getOptionalDocumentation(propertyNode);
127-
propTypes[propertyNode.key.name] = prop;
128-
}
129-
});
135+
propTypes = parsePropTypes(attributeNode.value, instanceOfResolver);
130136
}
131137
}
132138
});
139+
},
140+
'ExpressionStatement': (expressionNode: any): void => {
141+
if (expressionNode.expression.type == 'AssignmentExpression'
142+
&& expressionNode.expression.left.type == 'MemberExpression'
143+
&& expressionNode.expression.left.object.name == classname
144+
&& expressionNode.expression.left.property.name == 'propTypes') {
145+
propTypes = parsePropTypes(expressionNode.expression.right, instanceOfResolver);
146+
}
133147
}
134148
});
135149
return {
150+
exportType,
136151
classname,
137152
propTypes
138153
};
139154
}
140155

156+
function parsePropTypes(node: any, instanceOfResolver: InstanceOfResolver): IPropTypes {
157+
let propTypes: IPropTypes = {};
158+
walk(node, {
159+
'ObjectProperty': (propertyNode: any): void => {
160+
const prop: IProp = getTypeFromPropType(propertyNode.value, instanceOfResolver);
161+
prop.documentation = getOptionalDocumentation(propertyNode);
162+
propTypes[propertyNode.key.name] = prop;
163+
}
164+
});
165+
return propTypes;
166+
}
167+
141168
function getOptionalDocumentation(propertyNode: any): string {
142169
return (((propertyNode.leadingComments || []) as any[])
143170
.filter((comment: any) => comment.type == 'CommentBlock')[0] || {})
@@ -351,9 +378,12 @@ export class Writer {
351378
this.nl();
352379
}
353380

354-
public exportDefault(fn: () => void): void {
381+
public exportDeclaration(exportType: ExportType, fn: () => void): void {
355382
this.indent();
356-
this.code += 'export default ';
383+
this.code += 'export ';
384+
if (exportType == ExportType.default) {
385+
this.code += 'default ';
386+
}
357387
fn();
358388
}
359389

‎tests/simple-component.d.ts renamed to ‎tests/es6-class.d.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
declare module 'simple-component' {
1+
declare module 'component' {
22
import * as React from 'react';
33
import Message from './path/to/Message';
44

5-
interface SimpleComponentProps {
5+
interface ComponentProps {
66
key?: any;
77
/**
88
* This is a jsdoc comment for optionalAny.
@@ -25,6 +25,6 @@ declare module 'simple-component' {
2525
requiredArrayOf: string[];
2626
}
2727

28-
export default class SimpleComponent extends React.Component<SimpleComponentProps, any> {
28+
export class Component extends React.Component<ComponentProps, any> {
2929
}
3030
}

‎tests/es6-class.jsx

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as React from 'react';
2+
3+
export class Component extends React.Component {
4+
5+
render() {
6+
return (
7+
<div />
8+
);
9+
}
10+
}
11+
Component.propTypes = {
12+
/**
13+
* This is a jsdoc comment for optionalAny.
14+
*/
15+
optionalAny: React.PropTypes.any,
16+
optionalArray: React.PropTypes.array,
17+
optionalBool: React.PropTypes.bool,
18+
optionalFunc: React.PropTypes.func,
19+
optionalNumber: React.PropTypes.number,
20+
optionalObject: React.PropTypes.object,
21+
optionalString: React.PropTypes.string,
22+
optionalNode: React.PropTypes.node,
23+
optionalElement: React.PropTypes.element,
24+
optionalMessage: React.PropTypes.instanceOf(Message),
25+
//optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
26+
optionalUnion: React.PropTypes.oneOfType([
27+
React.PropTypes.string,
28+
React.PropTypes.number
29+
]),
30+
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
31+
//optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
32+
//optionalObjectWithShape: React.PropTypes.shape({
33+
// color: React.PropTypes.string,
34+
// fontSize: React.PropTypes.number
35+
//}),
36+
requiredFunc: React.PropTypes.func.isRequired,
37+
requiredAny: React.PropTypes.any.isRequired,
38+
requiredUnion: React.PropTypes.oneOfType([
39+
React.PropTypes.array,
40+
React.PropTypes.bool
41+
]).isRequired,
42+
requiredArrayOf: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
43+
};

‎tests/es7-class.d.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
declare module 'component' {
2+
import * as React from 'react';
3+
import Message from './path/to/Message';
4+
5+
interface ComponentProps {
6+
key?: any;
7+
/**
8+
* This is a jsdoc comment for optionalAny.
9+
*/
10+
optionalAny?: any;
11+
optionalArray?: any[];
12+
optionalBool?: boolean;
13+
optionalFunc?: (...args: any[]) => any;
14+
optionalNumber?: number;
15+
optionalObject?: Object;
16+
optionalString?: string;
17+
optionalNode?: React.ReactNode;
18+
optionalElement?: React.ReactElement<any>;
19+
optionalMessage?: typeof Message;
20+
optionalUnion?: string|number;
21+
optionalArrayOf?: number[];
22+
requiredFunc: (...args: any[]) => any;
23+
requiredAny: any;
24+
requiredUnion: any[]|boolean;
25+
requiredArrayOf: string[];
26+
}
27+
28+
export default class Component extends React.Component<ComponentProps, any> {
29+
}
30+
}

‎tests/simple-component.jsx renamed to ‎tests/es7-class.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22

3-
export default class SimpleComponent extends React.Component {
3+
export default class Component extends React.Component {
44

55
static propTypes = {
66
/**

‎tests/parsing-test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { assert } from 'chai';
2+
import * as fs from 'fs';
3+
import * as path from 'path';
4+
5+
import * as react2dts from '../index';
6+
7+
describe('Parsing', () => {
8+
it('should create definition from es6 class component', () => {
9+
const opts: react2dts.IOptions = {
10+
instanceOfResolver: (name: string): string => './path/to/Message'
11+
};
12+
assert.equal(
13+
react2dts.generateFromFile('component', path.join(__dirname, 'es6-class.jsx'), opts),
14+
fs.readFileSync(path.join(__dirname, 'es6-class.d.ts')).toString()
15+
);
16+
});
17+
it('should create definition from es7 class component', () => {
18+
const opts: react2dts.IOptions = {
19+
instanceOfResolver: (name: string): string => './path/to/Message'
20+
};
21+
assert.equal(
22+
react2dts.generateFromFile('component', path.join(__dirname, 'es7-class.jsx'), opts),
23+
fs.readFileSync(path.join(__dirname, 'es7-class.d.ts')).toString()
24+
);
25+
});
26+
});

‎tests/simple-component-test.ts

-17
This file was deleted.

‎tests/writer-test.ts

+8
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,12 @@ describe('The Writer', () => {
4040
writer.comment('* yada\n\t\t\t\tyada\n ');
4141
assert.equal(writer.toString(), '/** yada\nyada\n */\n');
4242
});
43+
it('should write an export default declaration', () => {
44+
writer.exportDeclaration(0, () => undefined);
45+
assert.equal(writer.toString(), 'export default ');
46+
});
47+
it('should write a named export declaration', () => {
48+
writer.exportDeclaration(1, () => undefined);
49+
assert.equal(writer.toString(), 'export ');
50+
});
4351
});

‎tsconfig.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"compilerOptions": {
3-
"target": "ES5",
3+
"target": "es5",
44
"module": "commonjs",
55
"noImplicitAny": true,
66
"sourceMap": true
77
},
88
"files": [
99
"typings/tsd.d.ts",
1010
"index.ts",
11-
"tests/simple-component-test.ts",
11+
"tests/parsing-test.ts",
1212
"tests/parse-prop-types-test.ts",
1313
"tests/writer-test.ts"
1414
]

0 commit comments

Comments
 (0)
This repository has been archived.