Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
coverage/
.nyc_output/
npm-debug.log
dist/
23 changes: 23 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"command": "npm",
"isShellCommand": true,
"showOutput": "silent",
"suppressTaskName": true,
"tasks": [
{
"taskName": "build",
"args": [
"run",
"build",
"--",
"--watch"
],
"echoCommand": true,
"isBuildCommand": true,
"isWatching": true
}
]
}
2 changes: 1 addition & 1 deletion cli.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env node
var react2dts = require('./index');
var react2dts = require('./dist/src/index');
var minimist = require('minimist');

var options = minimist(process.argv.slice(2), {
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
"name": "react-to-typescript-definitions",
"version": "0.13.0",
"description": "Create typescript definitions files (d.ts) from react components",
"main": "index.js",
"main": "dist/src/index.js",
"bin": {
"react2dts": "cli.js"
},
"files": [
"index.js",
"dist",
"cli.js",
"index.d.ts",
"index.js.map"
"index.d.ts"
],
"scripts": {
"start": "npm test",
Expand All @@ -19,7 +18,7 @@
"build": "tsc --sourceMap",
"build:inline": "tsc --inlineSourceMap",
"pretest": "npm run clean && npm run build:inline",
"test": "nyc --all --reporter lcov ./node_modules/.bin/mocha --require source-map-support --recursive tests/*-test.js",
"test": "nyc --all --reporter lcov ./node_modules/.bin/mocha --require source-map-support --recursive dist/tests/*-test.js",
"coverage": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -w",
"prerelease": "npm test && npm run build",
Expand Down Expand Up @@ -71,6 +70,7 @@
"exclude": [
"node_modules",
"coverage",
"dist/tests",
"tests"
]
}
Expand Down
41 changes: 24 additions & 17 deletions index.ts → src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ import * as babylon from 'babylon';
import * as dom from 'dts-dom';

export interface InstanceOfResolver {
(name: string): string;
(name: string): string|undefined;
};

export interface IOptions {

/**
* Resolves type names to import paths.
*
* @return Path to given name if resolveable, undefined otherwise
*/
instanceOfResolver?: InstanceOfResolver;

/**
* The Generator generating .d.ts code with.
*
* This option is deprecated with 0.13 and is not supported anymore.
* any new feature will not work with the deprecated Generator interface.

* @deprecated
*/
generator?: Generator;
Expand Down Expand Up @@ -71,11 +72,11 @@ export function cli(options: any): void {
});
}

export function generateFromFile(moduleName: string, path: string, options?: IOptions): string {
export function generateFromFile(moduleName: string|null, path: string, options?: IOptions): string {
return generateFromSource(moduleName, fs.readFileSync(path).toString(), options);
}

export function generateFromSource(moduleName: string, code: string, options: IOptions = {}): string {
export function generateFromSource(moduleName: string|null, code: string, options: IOptions = {}): string {
const ast = babylon.parse(code, {
sourceType: 'module',
allowReturnOutsideFunction: true,
Expand All @@ -101,9 +102,9 @@ export function generateFromSource(moduleName: string, code: string, options: IO
return generateFromAst(moduleName, ast, options);
}

const defaultInstanceOfResolver: InstanceOfResolver = (name: string): string => undefined;
const defaultInstanceOfResolver: InstanceOfResolver = (_name: string): undefined => undefined;

export function generateFromAst(moduleName: string, ast: any, options: IOptions = {}): string {
export function generateFromAst(moduleName: string|null, ast: any, options: IOptions = {}): string {
const parsingResult = parseAst(ast, options.instanceOfResolver);
if (options.generator) {
return deprecatedGenerator(options.generator, moduleName, parsingResult);
Expand All @@ -117,7 +118,7 @@ export function generateFromAst(moduleName: string, ast: any, options: IOptions
if (propTypes) {
Object.keys(propTypes).forEach(propName => {
const prop = propTypes[propName];
if (prop.importPath) {
if (prop.importType && prop.importPath) {
code += dom.emit(dom.create.importDefault(prop.importType, prop.importPath));
}
});
Expand All @@ -135,7 +136,7 @@ export function generateFromAst(moduleName: string, ast: any, options: IOptions
if (propTypes) {
Object.keys(propTypes).forEach(propName => {
const prop = propTypes[propName];
if (prop.importPath) {
if (prop.importType && prop.importPath) {
m.members.push(dom.create.importDefault(prop.importType, prop.importPath));
}
});
Expand Down Expand Up @@ -190,14 +191,14 @@ function createReactClassDeclaration(classname: string, exportType: ExportType,
return classDecl;
}

function deprecatedGenerator(generator: Generator, moduleName: string,
function deprecatedGenerator(generator: Generator, moduleName: string|null,
{exportType, classname, propTypes}: IParsingResult): string {
const generateTypings = () => {
generator.import('* as React', 'react');
if (propTypes) {
Object.keys(propTypes).forEach(propName => {
const prop = propTypes[propName];
if (prop.importPath) {
if (prop.importType && prop.importPath) {
generator.import(prop.importType, prop.importPath);
}
});
Expand Down Expand Up @@ -229,15 +230,15 @@ interface IParsingResult {
propTypes: IPropTypes;
}

function parseAst(ast: any, instanceOfResolver: InstanceOfResolver): IParsingResult {
let exportType: ExportType;
let classname: string;
function parseAst(ast: any, instanceOfResolver?: InstanceOfResolver): IParsingResult {
let exportType: ExportType|undefined;
let classname: string|undefined;
let propTypes: IPropTypes = {};
walk(ast.program, {
'ExportNamedDeclaration': exportNode => {
'ExportNamedDeclaration': () => {
exportType = ExportType.named;
},
'ExportDefaultDeclaration': exportNode => {
'ExportDefaultDeclaration': () => {
exportType = ExportType.default;
},
'ClassDeclaration': classNode => {
Expand Down Expand Up @@ -275,14 +276,20 @@ function parseAst(ast: any, instanceOfResolver: InstanceOfResolver): IParsingRes
}
});
}
if (exportType === undefined) {
throw new Error('No exported class found');
}
if (!classname) {
throw new Error('Anonymous classes are not supported');
}
return {
exportType,
classname,
propTypes
};
}

function parsePropTypes(node: any, instanceOfResolver: InstanceOfResolver): IPropTypes {
function parsePropTypes(node: any, instanceOfResolver?: InstanceOfResolver): IPropTypes {
let propTypes: IPropTypes = {};
walk(node, {
'ObjectProperty': propertyNode => {
Expand Down Expand Up @@ -369,7 +376,7 @@ function isRequiredPropType(node: any, instanceOfResolver: InstanceOfResolver):
}

/**
* This is for internal use only
* @internal
*/
export function getTypeFromPropType(node: IASTNode, instanceOfResolver = defaultInstanceOfResolver): IProp {
const result: IProp = {
Expand Down
Empty file removed tests/generator-test.d.ts
Empty file.
18 changes: 12 additions & 6 deletions tests/generator-test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert } from 'chai';
import { Generator, generateFromSource } from '../index';
import { Generator, generateFromSource } from '../src/index';

describe('The Generator', () => {
let generator: Generator;
Expand Down Expand Up @@ -58,25 +58,31 @@ describe('Generating typings with given custom generator', () => {
});

it('should delare a module if name given', () => {
let name: string = undefined;
let name: string|undefined;
generator.declareModule = moduleName => {
name = moduleName;
};

generateFromSource('module', '', {generator});
const source = `
export class Test {}
`;
generateFromSource('module', source, {generator});

assert.equal(name, 'module');
});

it('should import react', () => {
let decl: string = undefined;
let from: string = undefined;
let decl: string|undefined;
let from: string|undefined;
generator.import = (_decl, _from) => {
decl = _decl;
from = _from;
};

generateFromSource(null, '', {generator});
const source = `
export class Test {}
`;
generateFromSource(null, source, {generator});

assert.equal(decl, '* as React');
assert.equal(from, 'react');
Expand Down
Empty file removed tests/parse-prop-types-test.d.ts
Empty file.
4 changes: 2 additions & 2 deletions tests/parse-prop-types-test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert } from 'chai';
import * as dom from 'dts-dom';
import { getTypeFromPropType, IProp } from '../index';
import { getTypeFromPropType, IProp } from '../src/index';

describe('The PropType parser', () => {
const instanceOfResolver = (): any => undefined;
Expand Down Expand Up @@ -265,7 +265,7 @@ describe('The PropType parser', () => {
}
]
};
const result: IProp = getTypeFromPropType(ast, (name: string): string => './some/path');
const result: IProp = getTypeFromPropType(ast, (): string => './some/path');
assert.equal(result.type, 'typeof Message');
assert.deepEqual(result.type2, dom.create.typeof(dom.create.namedTypeReference('Message')));
assert.equal(result.optional, true);
Expand Down
Empty file removed tests/parsing-test.d.ts
Empty file.
28 changes: 15 additions & 13 deletions tests/parsing-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import * as path from 'path';
import * as chalk from 'chalk';
import * as diff from 'diff';

import * as react2dts from '../index';
import * as react2dts from '../src/index';

const basedir = path.join(__dirname, '..', '..', 'tests');

function textDiff(actual: string, expected: string): void {
const differences = diff.diffChars(expected, actual);
Expand All @@ -22,38 +24,38 @@ function textDiff(actual: string, expected: string): void {
describe('Parsing', () => {
it('should create definition from es6 class component', () => {
const opts: react2dts.IOptions = {
instanceOfResolver: (name: string): string => './path/to/Message'
instanceOfResolver: (): string => './path/to/Message'
};
textDiff(
react2dts.generateFromFile('component', path.join(__dirname, 'es6-class.jsx'), opts),
fs.readFileSync(path.join(__dirname, 'es6-class.d.ts')).toString()
react2dts.generateFromFile('component', path.join(basedir, 'es6-class.jsx'), opts),
fs.readFileSync(path.join(basedir, 'es6-class.d.ts')).toString()
);
});
it('should create definition from es7 class component', () => {
const opts: react2dts.IOptions = {
instanceOfResolver: (name: string): string => './path/to/Message'
instanceOfResolver: (): string => './path/to/Message'
};
textDiff(
react2dts.generateFromFile('component', path.join(__dirname, 'es7-class.jsx'), opts),
fs.readFileSync(path.join(__dirname, 'es7-class.d.ts')).toString()
react2dts.generateFromFile('component', path.join(basedir, 'es7-class.jsx'), opts),
fs.readFileSync(path.join(basedir, 'es7-class.d.ts')).toString()
);
});
it('should create top-level module definition from es7 class component', () => {
const opts: react2dts.IOptions = {
instanceOfResolver: (name: string): string => './path/to/Message'
instanceOfResolver: (): string => './path/to/Message'
};
textDiff(
react2dts.generateFromFile(null, path.join(__dirname, 'es7-class.jsx'), opts),
fs.readFileSync(path.join(__dirname, 'es7-class-top-level-module.d.ts')).toString()
react2dts.generateFromFile(null, path.join(basedir, 'es7-class.jsx'), opts),
fs.readFileSync(path.join(basedir, 'es7-class-top-level-module.d.ts')).toString()
);
});
it('should create definition from babeled es7 class component', () => {
const opts: react2dts.IOptions = {
instanceOfResolver: (name: string): string => './path/to/Message'
instanceOfResolver: (): string => './path/to/Message'
};
textDiff(
react2dts.generateFromFile('component', path.join(__dirname, 'es7-class-babeled.js'), opts),
fs.readFileSync(path.join(__dirname, 'es7-class.d.ts')).toString()
react2dts.generateFromFile('component', path.join(basedir, 'es7-class-babeled.js'), opts),
fs.readFileSync(path.join(basedir, 'es7-class.d.ts')).toString()
);
});
});
19 changes: 15 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"noImplicitAny": true,
"declaration": true,
"listFiles": false,
"pretty": true
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"pretty": true,
"strictNullChecks": true,
"stripInternal": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5"
},
"exclude": [
"index.d.ts",
"dist",
"node_modules",
"tests/es6-class.d.ts",
"tests/es7-class.d.ts",
Expand Down