master 7444ece835eb cached
87 files
102.8 KB
28.9k tokens
192 symbols
1 requests
Download .txt
Repository: gregjacobs/js-to-ts-converter
Branch: master
Commit: 7444ece835eb
Files: 87
Total size: 102.8 KB

Directory structure:
gitextract_7dpmj4fc/

├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── src/
│   ├── cli.ts
│   ├── converter/
│   │   ├── add-class-property-declarations/
│   │   │   ├── add-class-property-declarations.ts
│   │   │   ├── correct-js-properties.ts
│   │   │   ├── js-class.ts
│   │   │   ├── parse-js-classes.ts
│   │   │   └── parse-superclass-name-and-path.ts
│   │   ├── add-optionals-to-function-params.ts
│   │   ├── convert.ts
│   │   └── filter-out-node-modules.ts
│   ├── create-ts-morph-project.ts
│   ├── index.ts
│   ├── js-to-ts-converter.ts
│   ├── logger/
│   │   ├── index.ts
│   │   ├── log-level.ts
│   │   └── logger.ts
│   └── util/
│       ├── find-import-for-identifier.ts
│       ├── is-element-access-with-obj.ts
│       ├── is-property-access-with-obj.ts
│       ├── is-property-or-elemement-access-with-obj.ts
│       ├── is-this-referencing-var.ts
│       ├── is-valid-identifier.ts
│       ├── parse-destructured-props.ts
│       └── set-utils.ts
├── test/
│   ├── convert.spec.ts
│   └── fixture/
│       ├── class-with-this-constructor-reference/
│       │   ├── expected/
│       │   │   └── my-class.ts
│       │   └── input/
│       │       └── my-class.js
│       ├── expression-extends/
│       │   ├── expected/
│       │   │   └── expression-extends.ts
│       │   └── input/
│       │       └── expression-extends.js
│       ├── function-calls-with-fewer-args-than-params/
│       │   ├── expected/
│       │   │   ├── call-to-exported-function.ts
│       │   │   ├── call-to-local-function-with-default-value.ts
│       │   │   ├── call-to-local-function.ts
│       │   │   ├── call-to-sub-class-method.ts
│       │   │   ├── call-to-super-class-method.ts
│       │   │   ├── constructor-with-rest-param.ts
│       │   │   ├── exported-function.ts
│       │   │   ├── sub-class.ts
│       │   │   └── super-class.ts
│       │   └── input/
│       │       ├── call-to-exported-function.js
│       │       ├── call-to-local-function-with-default-value.js
│       │       ├── call-to-local-function.js
│       │       ├── call-to-sub-class-method.js
│       │       ├── call-to-super-class-method.js
│       │       ├── constructor-with-rest-param.js
│       │       ├── exported-function.js
│       │       ├── sub-class.js
│       │       └── super-class.js
│       ├── function-expressions-and-declarations/
│       │   ├── expected/
│       │   │   └── class-with-function-expressions.ts
│       │   └── input/
│       │       └── class-with-function-expressions.js
│       ├── include-exclude-patterns/
│       │   ├── expected/
│       │   │   └── included/
│       │   │       └── included-file.ts
│       │   └── input/
│       │       ├── included/
│       │       │   ├── excluded/
│       │       │   │   └── excluded-file.js
│       │       │   └── included-file.js
│       │       └── other-file-that-should-not-be-included.js
│       ├── react-class-js/
│       │   ├── expected/
│       │   │   └── react-class.tsx
│       │   └── input/
│       │       └── react-class.js
│       ├── react-class-jsx/
│       │   ├── expected/
│       │   │   └── react-class.tsx
│       │   └── input/
│       │       └── react-class.jsx
│       ├── react-jsx-self-closing-element/
│       │   ├── expected/
│       │   │   └── react-self-closing-element.tsx
│       │   └── input/
│       │       └── react-self-closing-element.js
│       ├── superclass-subclass/
│       │   ├── expected/
│       │   │   ├── another-sub-class.ts
│       │   │   ├── default-export-class.ts
│       │   │   ├── my-class.ts
│       │   │   ├── my-sub-class.ts
│       │   │   ├── my-super-class.ts
│       │   │   ├── superclass-in-node-modules.ts
│       │   │   └── two-classes.ts
│       │   └── input/
│       │       ├── another-sub-class.js
│       │       ├── default-export-class.js
│       │       ├── my-class.js
│       │       ├── my-sub-class.js
│       │       ├── my-super-class.js
│       │       ├── package.json
│       │       ├── superclass-in-node-modules.js
│       │       └── two-classes.js
│       ├── superclass-subclass-node-modules-not-installed/
│       │   ├── expected/
│       │   │   └── superclass-in-node-modules.ts
│       │   └── input/
│       │       └── superclass-in-node-modules.js
│       └── typescript-class/
│           ├── expected/
│           │   ├── declarations-in-superclass.ts
│           │   ├── typescript-class.ts
│           │   └── typescript-sub-class.ts
│           └── input/
│               ├── declarations-in-superclass.ts
│               ├── typescript-class.ts
│               └── typescript-sub-class.ts
├── test.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/.idea
/dist
/node_modules
/yarn-error.log

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 Gregory Jacobs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# js-to-ts-converter

Small utility that I wrote to script converting a JS codebase to TypeScript,
while trying to solve some of the common TypeScript errors that will be received
upon such a conversion.

The utility performs the following transformations:

1. Renames `.js` files to `.ts`
2. Adds property declarations to ES6 classes so that they are compilable by the
   TypeScript compiler (see below).
3. Any function calls that provide fewer arguments than the declared parameters
   in the function will cause the remaining parameters to be marked as optional
   for that function. This solves TS errors like "Expected 3 arguments, but 
   got 2"
   
Note: because this utility utilizes the TypeScript Language Service to perform
the look-ups for #3, it may take a long time to run. For a small project, expect
a few minutes. For a larger project, it could take tens of minutes. Still much 
better than the days/weeks it could take to fix an entire codebase by hand :)
 

For #2 above, the utility basically looks at any `this` property accessed by a 
JS class, and fills in the appropriate TypeScript property declarations. Take 
this `.js` input source file as an example:

```js
class Super {
	someMethod() {
		this.superProp = 1;
	}
}

class Sub extends Super {
	someMethod() {
		this.superProp = 2;
		this.subProp = 2;
	}
}
```


The above JS classes are replaced with the following TS classes:

```ts
class Super {
    public superProp: any;   // <-- added

    someMethod() {
        this.superProp = 1;
    }
}


class Sub extends Super {
    public subProp: any;    // <-- added

    someMethod() {
        this.superProp = 2;
        this.subProp = 2;
    }
}
```

Note: properties used when `this` is assigned to another variable are also 
found for purposes of creating property declarations. Example:

```js
class MyClass {
    myMethod() {
        var that = this;
        
        that.something;  // <-- 'something' parsed as a class property
    }
}
```

For #3 above, parameters are marked as 'optional' when there are callers that
don't provide all of them. For example, the following JavaScript:

```js
function myFunction( arg1, arg2 ) {
	// ...
}

myFunction( 1 );  // only provide arg1
```

Will be transformed to the following TypeScript:

```ts
function myFunction( arg1, arg2? ) {  // <-- arg2 marked as optional
	// ...
}

myFunction( 1 );
```

## Goal

The goal of this utility is to simply make the `.js` code compilable under the
TypeScript compiler, so simply adding the property declarations typed as `any` 
was the quickest option there. The utility may look at property initializers in 
the future to determine a better type.

If you have other types of compiler errors that you think might be able to be 
transformed by this utility, please feel free to raise an issue (or pull
request!)

Hopefully you only need to use this utility once, but if it saved you time, 
please star it so that I know it helped you out :)


## Fair Warning

This utility makes modifications to the directory that you pass it. Make sure
you are in a clean git (or other VCS) state before running it in case you need
to revert!


## Running the Utility from the CLI

```
npx js-to-ts-converter ./path/to/js/files
```

If you would prefer to install the CLI globally, do this:

```
npm install --global js-to-ts-converter

js-to-ts-converter ./path/to/js/files
```


## Running the Utility from Node

TypeScript: 

```ts
import { convertJsToTs, convertJsToTsSync } from 'js-to-ts-converter';


// Async
convertJsToTs( 'path/to/js/files' ).then( 
    () => console.log( 'Done!' ),
    ( err ) => console.log( 'Error: ', err );
); 


// Sync
convertJsToTsSync( 'path/to/js/files' );
console.log( 'Done!' );
```

JavaScript:

```js
const { convertJsToTs, convertJsToTsSync } = require( 'js-to-ts-converter' );


// Async
convertJsToTs( 'path/to/js/files' ).then( 
    () => console.log( 'Done!' ),
    ( err ) => console.log( 'Error: ', err );
); 


// Sync
convertJsToTsSync( 'path/to/js/files' );
console.log( 'Done!' );
```

## Developing

Make sure you have [Node.js](https://nodejs.org) installed. 

Clone the git repo: 

```
git clone https://github.com/gregjacobs/js-to-ts-converter.git

cd js-to-ts-converter
```

Install dependencies:

```
npm install
```

Run Tests:

```
npm test
```


================================================
FILE: package.json
================================================
{
	"name": "js-to-ts-converter",
	"version": "0.18.2",
	"description": "Small utility to rename .js->.ts, and convert ES6 classes to TypeScript classes by filling in property declarations. See readme for more details.",
	"scripts": {
		"build": "tsc",
		"cli": "npm run build && node dist/cli.js",
		"test": "mocha --require ts-node/register --timeout 60000 --watch-extensions ts \"**/*.spec.ts\"",
		"prepublishOnly": "npm test && npm run build"
	},
	"keywords": [
		"typescript",
		"conversion"
	],
	"homepage": "https://github.com/gregjacobs/js-to-ts-converter",
	"repository": {
		"type": "git",
		"url": "https://github.com/gregjacobs/js-to-ts-converter.git"
	},
	"bugs": {
		"url": "https://github.com/gregjacobs/js-to-ts-converter/issues"
	},
	"author": "Gregory Jacobs <greg@greg-jacobs.com>",
	"license": "MIT",
	"main": "dist/index.js",
	"types": "dist/index.d.ts",
	"bin": "dist/cli.js",
	"files": [
		"dist/"
	],
	"dependencies": {
		"argparse": "^1.0.10",
		"fast-glob": "^3.2.4",
		"graphlib": "^2.1.5",
		"lodash": "^4.17.20",
		"resolve": "^1.8.1",
		"trace-error": "^0.0.7",
		"ts-morph": "^14.0.0",
		"typescript": "^4.6.2",
		"winston": "^3.0.0"
	},
	"devDependencies": {
		"@types/chai": "^4.1.4",
		"@types/graphlib": "^2.1.4",
		"@types/lodash": "^4.14.112",
		"@types/mocha": "^5.2.4",
		"@types/node": "^10.5.2",
		"@types/winston": "^2.3.9",
		"chai": "^4.1.2",
		"mocha": "^5.2.0",
		"ts-node": "^7.0.0"
	}
}


================================================
FILE: src/cli.ts
================================================
#!/usr/bin/env node

import * as path from "path";
import * as fs from 'fs';

import { IndentationText } from "ts-morph";
import logger, { LogLevel, logLevels } from './logger';
import { convertJsToTsSync } from "./js-to-ts-converter";


const ArgumentParser = require('argparse').ArgumentParser;
const parser = new ArgumentParser( {
	version: require( '../package.json' ).version,
	addHelp: true,
	description: 'JS -> TS Converter'
} );
parser.addArgument( 'directory', {
	help: 'The directory of .js files to convert'
} );
parser.addArgument( '--indentation-text', {
	help: 'How you would like new code to be indented',
	choices: [ 'tab', 'twospaces', 'fourspaces', 'eightspaces' ],
	defaultValue: 'tab'
} );
parser.addArgument( '--include', {
	help: 'Glob patterns to include in the conversion. Separate multiple patterns ' +
	      'with a comma. The patterns must be valid for the "glob-all" npm ' +
	      'package (https://www.npmjs.com/package/glob-all), and are relative to ' +
	      'the input directory.\n' +
	      'Example: --include="**/myFolder/**,**/*.js"'
} );
parser.addArgument( '--exclude', {
	help: 'Glob patterns to exclude from being converted. Separate multiple patterns ' +
	      'with a comma. The patterns must be valid for the "glob-all" npm ' +
	      'package (https://www.npmjs.com/package/glob-all), and are relative to ' +
	      'the input directory.\n' +
	      'Example: --exclude="**/myFolder/**,**/*.jsx"'
} );
parser.addArgument( '--log-level', {
	help: `
		The level of logs to print to the console. From highest amount of \
		logging to lowest amount of logging: '${logLevels.join("', '")}' 
		Defaults to verbose to tell you what's going on, as the script may take
		a long time to complete when looking up usages of functions. Use 'debug'
		to enable even more logging.
	`.trim().replace( /^\t*/gm, '' ),
	choices: logLevels,
	defaultValue: 'verbose'
} );

const args = parser.parseArgs();
const absolutePath = path.resolve( args.directory.replace( /\/$/, '' ) );  // remove any trailing slash


if( !fs.lstatSync( absolutePath ).isDirectory() ) {
	logger.error( `${absolutePath} is not a directory. Please provide a directory` );
	process.exit( 1 );
} else {
	logger.info( `Processing directory: '${absolutePath}'` );
}


convertJsToTsSync( absolutePath, {
	indentationText: resolveIndentationText( args.indentation_text ),
	logLevel: resolveLogLevel( args.log_level ),
	includePatterns: parseIncludePatterns( args.include ),
	excludePatterns: parseExcludePatterns( args.exclude )
} );


/**
 * Private helper to resolve the correct IndentationText enum from the CLI
 * 'indentation' argument.
 */
function resolveIndentationText( indentationText: 'tab' | 'twospaces' | 'fourspaces' | 'eightspaces' ) {
	switch( indentationText ) {
		case 'tab'         : return IndentationText.Tab;
		case 'twospaces'   : return IndentationText.TwoSpaces;
		case 'fourspaces'  : return IndentationText.FourSpaces;
		case 'eightspaces' : return IndentationText.EightSpaces;

		default : return IndentationText.Tab;
	}
}


function resolveLogLevel( logLevelStr: string ): LogLevel {
	if( !logLevels.includes( logLevelStr ) ) {
		throw new Error( `
			Unknown --log-level argument '${logLevelStr}'
			Must be one of: '${logLevels.join( "', '" )}'
		`.trim().replace( /\t*/gm, '' ) );
	}

	return logLevelStr as LogLevel;
}


function parseIncludePatterns( includePatterns: string | undefined ): string[] | undefined {
	if( !includePatterns ) { return undefined; }  // return undefined to use the default

	return includePatterns.split( ',' );
}

function parseExcludePatterns( excludePatterns: string | undefined ): string[] {
	if( !excludePatterns ) { return []; }

	return excludePatterns.split( ',' );
}

================================================
FILE: src/converter/add-class-property-declarations/add-class-property-declarations.ts
================================================
import { Project, ClassInstancePropertyTypes, PropertyDeclarationStructure, Scope } from "ts-morph";
import { parseJsClasses } from "./parse-js-classes";
import { correctJsProperties } from "./correct-js-properties";
import logger from "../../logger/logger";

/**
 * Parses all source files looking for ES6 classes, and takes any `this`
 * property access to create a PropertyDeclaration for the class.
 *
 * For example:
 *
 *     class Something {
 *         constructor() {
 *             this.someProp = 1;
 *         }
 *     }
 *
 * Is changed to:
 *
 *     class Something {
 *         someProp: any;
 *
 *         constructor() {
 *             this.someProp = 1;
 *         }
 *     }
 */
export function addClassPropertyDeclarations( tsAstProject: Project ): Project {
	// Parse the JS classes for all of the this.xyz properties that they use
	const jsClasses = parseJsClasses( tsAstProject );

	// Correct the JS classes' properties for superclass/subclass relationships
	// (essentially remove properties from subclasses that are defined by their
	// superclasses)
	const propertiesCorrectedJsClasses = correctJsProperties( jsClasses );

	// Fill in field definitions for each of the classes
	propertiesCorrectedJsClasses.forEach( jsClass => {
		const sourceFile = tsAstProject.getSourceFileOrThrow( jsClass.path );
		logger.verbose( `  Updating class '${jsClass.name}' in '${sourceFile.getFilePath()}'` );

		const classDeclaration = sourceFile.getClassOrThrow( jsClass.name! );
		const jsClassProperties = jsClass.properties;

		// If the utility was run against a TypeScript codebase, we should not
		// fill in property declarations for properties that are already
		// declared in the class. However, we *should* fill in any missing
		// declarations. Removing any already-declared declarations from the
		// jsClassProperties.
		const currentPropertyDeclarations = classDeclaration.getInstanceProperties()
			.reduce( ( props: Set<string>, prop: ClassInstancePropertyTypes ) => {
				const propName = prop.getName();
				return propName ? props.add( propName ) : props;
			}, new Set<string>() );

		let undeclaredProperties = [ ...jsClassProperties ]
			.filter( ( propName: string ) => !currentPropertyDeclarations.has( propName ) );

		// If the utility found a reference to this.constructor, we don't want to
		// add a property called 'constructor'. Filter that out now.
		// https://github.com/gregjacobs/js-to-ts-converter/issues/9
		undeclaredProperties = undeclaredProperties
			.filter( ( propName: string ) => propName !== 'constructor' );

		// Add all currently-undeclared properties
		const propertyDeclarations = undeclaredProperties.map( propertyName => {
			return {
				name: propertyName,
				type: 'any',
				scope: Scope.Public
			} as PropertyDeclarationStructure;
		} );

		logger.verbose( `    Adding property declarations for properties: '${undeclaredProperties.join( "', '" )}'` );
		classDeclaration.insertProperties( 0, propertyDeclarations );
	} );

	return tsAstProject;
}

================================================
FILE: src/converter/add-class-property-declarations/correct-js-properties.ts
================================================
import { JsClass } from "./js-class";
import { Graph, alg } from "graphlib";
import logger from "../../logger/logger";

/**
 * After the graph of original {@link JsClass}es and their properties have been
 * created, we need to remove properties from subclasses that are defined in
 * their superclasses.
 *
 * This function takes the original graph of classes with all properties in each
 * class and returns a new list of JsClasses with the properties properly
 * filtered so that subclasses do not define the same properties that are
 * already present in their superclasses.
 *
 * ## Algorithm
 *
 * 1. Build graph of subclasses -> superclasses
 * 2. Take topological sort of graph
 * 3. Starting at the superclasses in the sort, fill in the
 *    propertySets for each JsClass. For every subclass encountered,
 *    filter out its superclass properties to create the subclass's property
 *    set
 * 4. Use the propertySets to create a new list of JsClasses
 */
export function correctJsProperties( jsClasses: JsClass[] ): JsClass[] {
	logger.debug( 'Building graph of class hierarchy to determine which class properties belong to superclasses/subclasses' );

	const jsClassHierarchyGraph = new Graph();

	// First, add all nodes to the graph
	jsClasses.forEach( jsClass => {
		jsClassHierarchyGraph.setNode( jsClass.id, jsClass );
	} );

	// Second, connect the subclasses to superclasses in the graph
	jsClasses.forEach( jsClass => {
		if( jsClass.superclassId ) {
			// If we come across a JsClass whose superclass is in the node_modules
			// directory (i.e. imported from another package), do not try to
			// go into that package. We're not going to try to understand an ES5
			// module
			if( jsClass.isSuperclassInNodeModules() ) {
				return;
			}

			// As a bit of error checking, make sure that we're not going to
			// accidentally create a graph node by adding an edge to
			// jsClass.superclassId. This would happen if we didn't figure out
			// the correct path to the superclass in the parse phase, or we
			// didn't have the superclass's source file added to the project.
			if( !jsClassHierarchyGraph.hasNode( jsClass.superclassId ) ) {
				throw new Error( `
					An error occurred while adding property declarations to class
					'${jsClass.name}' in file:
					    '${jsClass.path}'
					
					Did not parse this class's superclass ('${jsClass.superclassName}') from file:
					    '${jsClass.superclassPath}'
					during the parse phase. 
					
					Make sure that this class's superclass's .js file is within the 
					directory passed to this conversion utility, or otherwise 
					there is a bug in this utility. Please report at:
					    https://github.com/gregjacobs/js-to-ts-converter/issues
					 
					Debugging info:
					
					This class's graph ID: ${jsClass.id}
					It's superclass's graph ID: ${jsClass.superclassId}
					
					Current IDs in the graph:
					    ${jsClassHierarchyGraph.nodes().join( '\n    ' )}
				`.replace( /^\t*/gm, '' ) );
			}

			jsClassHierarchyGraph.setEdge( jsClass.id, jsClass.superclassId );
		}
	} );

	// the topological sort is going to put superclasses later in the returned
	// array, so reverse it
	logger.debug( 'Topologically sorting the graph in superclass->subclass order' );
	const superclassToSubclassOrder = alg.topsort( jsClassHierarchyGraph ).reverse();

	// Starting from superclass JsClass instances and walking down to subclass
	// JsClass instances, fill in the property sets. When a subclass is
	// encountered, take all of the properties that were used in that subclass,
	// minus the properties in its superclass, in order to determine the
	// subclass-specific properties
	superclassToSubclassOrder.forEach( jsClassId => {
		const jsClass = jsClassHierarchyGraph.node( jsClassId ) as JsClass;
		const subclassOnlyProperties = new Set<string>( jsClass.properties );

		const superclasses = getSuperclasses( jsClass );
		superclasses.forEach( ( superclass: JsClass ) => {
			// Filter out both properties and methods from each superclass
			superclass.members.forEach( ( superclassProp: string ) => {
				subclassOnlyProperties.delete( superclassProp );
			} );
		} );

		const newJsClass = new JsClass( {
			path: jsClass.path,
			name: jsClass.name,
			superclassName: jsClass.superclassName,
			superclassPath: jsClass.superclassPath,
			methods: jsClass.methods,
			properties: subclassOnlyProperties
		} );

		// Re-assign the new JsClass with the correct subclass properties back
		// to the graph for the next iteration, in case there is a subclass of
		// the current class which needs to read those properties
		jsClassHierarchyGraph.setNode( jsClassId, newJsClass );
	} );


	// Return all of the new JsClass instances with properties corrected for
	// superclass/subclasses
	return jsClassHierarchyGraph.nodes()
		.map( jsClassId => jsClassHierarchyGraph.node( jsClassId ) as JsClass );


	function getSuperclasses( jsClass: JsClass ) {
		const superclasses: JsClass[] = [];

		while( jsClass.superclassId ) {
			const superclass = jsClassHierarchyGraph.node( jsClass.superclassId ) as JsClass;
			superclasses.push( superclass );

			jsClass = superclass;
		}
		return superclasses;
	}
}

================================================
FILE: src/converter/add-class-property-declarations/js-class.ts
================================================
import { union } from "../../util/set-utils";

/**
 * Represents a JavaScript class found in a source file.
 */
export class JsClass {
	/**
	 * The name of the class.
	 *
	 * Will be undefined for a default export class.
	 */
	public readonly name: string | undefined;

	/**
	 * The absolute path of the file that the class was found in.
	 */
	public readonly path: string;

	/**
	 * The name of this class's superclass. Will be `undefined` if the class
	 * does not have a superclass.
	 */
	public readonly superclassName: string | undefined;

	/**
	 * The path to the file which holds this class's superclass. If the same
	 * file that holds this class also holds its superclass, this will be the
	 * same value as the {@link #path}.
	 *
	 * Will be `undefined` if the superclass was found in the node_modules
	 * folder. We don't try to resolve the path of a module that exists in the
	 * node_modules folder as they're not relevant to this conversion utility,
	 * and we want to allow conversions of codebases that don't have
	 * node_modules installed (which can really improve performance as
	 * ts-morph doesn't try to resolve them when it finds imports in .ts
	 * files)
	 */
	public readonly superclassPath: string | undefined;

	/**
	 * The set of methods found in the class.
	 */
	public readonly methods: Set<string>;

	/**
	 * The set of properties found to be used in the class. These are inferred
	 * from usages. For example: console.log(this.something) would tell us that
	 * the class has a property `something`
	 */
	public readonly properties: Set<string>;

	/**
	 * A union of the {@link #methods} and {@link #properties} sets
	 */
	public readonly members: Set<string>;

	constructor( cfg: {
		name: string | undefined;
		path: string;
		superclassName: string | undefined,
		superclassPath: string | undefined,
		methods?: Set<string>;
		properties?: Set<string>;
	} ) {
		this.name = cfg.name;
		this.path = cfg.path;
		this.superclassName = cfg.superclassName;
		this.superclassPath = cfg.superclassPath;
		this.methods = cfg.methods || new Set<string>();
		this.properties = cfg.properties || new Set<string>();

		this.members = union( this.methods, this.properties );
	}

	/**
	 * String identifier for the JsClass which is a combination of its file path
	 * and class name. Used to store JsClass nodes on a graphlib Graph.
	 */
	public get id(): string {
		return `${this.path}_${this.name}`;
	}

	/**
	 * Retrieves the ID of the superclass JsClass instance, if the JsClass has
	 * one. If not, returns undefined.
	 *
	 * Also returns `undefined` if the class is found to be in the node_modules
	 * folder, as we don't want to attempt to parse ES5 modules.
	 */
	public get superclassId(): string | undefined {
		if( this.isSuperclassInNodeModules() ) {
			// If the superclass is in the node_modules folder, we'll
			// essentially treat this JsClass as if it didn't have a superclass.
			// See `isSuperclassInNodeModules()` jsdoc for details.
			return undefined;

		} else {
			return this.superclassName && `${this.superclassPath}_${this.superclassName}`;
		}
	}


	/**
	 * Determines if the JsClass's superclass was found in the node_modules
	 * directory (i.e. it extends from another package).
	 *
	 * If so, we're not going to try to understand a possibly ES5 module for
	 * its properties, so we'll just stop processing at that point.
	 */
	public isSuperclassInNodeModules(): boolean {
		return this.superclassPath === undefined;
	}

}

================================================
FILE: src/converter/add-class-property-declarations/parse-js-classes.ts
================================================
import { Project, ts, ClassDeclaration, ClassInstancePropertyTypes, MethodDeclaration, PropertyAccessExpression, SourceFile, SyntaxKind, VariableDeclaration } from "ts-morph";
import { JsClass } from "./js-class";
import { difference, union } from "../../util/set-utils";
import { parseDestructuredProps } from "../../util/parse-destructured-props";
import { parseSuperclassNameAndPath } from "./parse-superclass-name-and-path";
import { isThisReferencingVar } from "../../util/is-this-referencing-var";
import { propertyAccessWithObjFilter } from "../../util/is-property-access-with-obj";
import logger from "../../logger/logger";

/**
 * Parses the classes out of each .js file in the SourceFilesCollection, and
 * forms a tree representing their hierarchy.
 *
 * ## Description of algorithm:
 *
 * Each source file is parsed to find all file-level classes. Their superclasses
 * and import paths for those superclasses are also recorded to form an
 * adjacency list graph of classes keyed by their file path.
 *
 * Each class is also processed to find and record any property accesses of the
 * `this` object. For instance, in the following class, there are 3
 * PropertyAccessExpressions that pull from the `this` object ('something1',
 * 'something2', and 'something3'):
 *
 *     class Something {
 *         constructor() {
 *             this.something1 = 1;
 *             this.something2 = 2;
 *         }
 *
 *         someMethod() {
 *             console.log( this.something3 );
 *
 *             console.log( window.location );  // <-- not a `this` PropertyAccessExpression
 *         }
 *     }
 *
 * The returned graph will be used later to determine which TS class property
 * definitions should be placed in superclasses vs. subclasses. Properties used
 * by a superclass and a subclass should only be defined in the superclass.
 */
export function parseJsClasses( tsAstProject: Project ): JsClass[] {
	logger.verbose( "Parsing JS classes in the codebase..." );
	const files = tsAstProject.getSourceFiles();

	const jsClasses = files.reduce( ( classes: JsClass[], file: SourceFile ) => {
		logger.debug( `Parsing classes in file: ${file.getFilePath()}` );

		const fileClasses = parseFileClasses( file );
		return classes.concat( fileClasses );
	}, [] );

	return jsClasses;
}


/**
 * Parses the file-level classes out of the given `sourceFile`.
 */
function parseFileClasses( sourceFile: SourceFile ): JsClass[] {
	return sourceFile.getClasses().map( fileClass => {
		const className = fileClass.getName();

		logger.debug( `  Parsing class: ${className}` );

		const { superclassName, superclassPath } = parseSuperclassNameAndPath( sourceFile, fileClass );
		const methodNames = getMethodNames( fileClass );
		const propertyNames = getPropertyNames( fileClass );
		const propertiesMinusMethods = difference( propertyNames, methodNames );  // remove any method names from this Set

		return new JsClass( {
			path: sourceFile.getFilePath(),
			name: className,
			superclassName,
			superclassPath,
			methods: methodNames,
			properties: propertiesMinusMethods
		} );
	} );
}


/**
 * Parses the method names from the class into a Set of strings.
 */
function getMethodNames( fileClass: ClassDeclaration ): Set<string> {
	return fileClass.getMethods()
		.reduce( ( methods: Set<string>, method: MethodDeclaration ) => {
			return methods.add( method.getName() );
		}, new Set<string>() );
}


/**
 * Retrieves the list of propertyNames used in the class. This may also include
 * method names (which are technically properties), which we'll filter out later.
 */
function getPropertyNames( fileClass: ClassDeclaration ) {
	const existingPropertyDeclarations = parsePropertyDeclarations( fileClass );  // in case we are actually parsing a TypeScript class with existing declarations
	const propertyAccesses = parsePropertyAccesses( fileClass );
	const destructuringUsesOfProperties = parseDestructuringThisAssignments( fileClass );
	const propertyAccessesOfThisAssignedVars = parsePropertyAccessesOfThisAssignedVars( fileClass );

	return union(
		existingPropertyDeclarations,
		propertyAccesses,
		destructuringUsesOfProperties,
		propertyAccessesOfThisAssignedVars
	);
}


/**
 * In the case that the utility is actually parsing TypeScript classes with
 * existing property declarations, we want to know about these so we don't
 * accidentally write in new ones of the same name.
 */
function parsePropertyDeclarations( fileClass: ClassDeclaration ): Set<string> {
	return fileClass.getInstanceProperties()
		.reduce( ( props: Set<string>, prop: ClassInstancePropertyTypes ) => {
			const propName = prop.getName();
			return propName ? props.add( propName ) : props;  // don't add unnamed properties (not sure how we would have one of those, but seems its possible according to the TsSimpleAst types)
		}, new Set<string>() );
}


/**
 * Parses the property names of `this` PropertyAccessExpressions.
 *
 * Examples:
 *
 *     this.something = 42;
 *     console.log( this.something2 );
 *
 *     const { destructured1, destructured2 } = this;
 *
 * Method returns:
 *
 *    Set( [ 'something', 'something2', 'destructured1', 'destructured2' ] )
 */
function parsePropertyAccesses( fileClass: ClassDeclaration ): Set<string> {
	// First, find all of the `this.something` properties
	const thisProps = fileClass
		.getDescendantsOfKind( SyntaxKind.PropertyAccessExpression )
		.filter( ( prop: PropertyAccessExpression ) => prop.getExpression().getKind() === SyntaxKind.ThisKeyword );

	const propNamesSet = thisProps
		.reduce( ( props: Set<string>, prop: PropertyAccessExpression ) => {
			return props.add( prop.getName() );
		}, new Set<string>() );

	return propNamesSet;
}


/**
 * Parses any object destructuring statements of the form:
 *
 *     var { a, b } = this;
 *
 * And returns Set( [ 'a', 'b' ] ) in this case.
 */
function parseDestructuringThisAssignments( fileClass: ClassDeclaration ): Set<string> {
	// Second, find any `var { a, b } = this` statements
	const destructuredProps = fileClass
		.getDescendantsOfKind( SyntaxKind.VariableDeclaration )
		.filter( ( varDec: VariableDeclaration ) => {
			return varDec.compilerNode.name.kind === SyntaxKind.ObjectBindingPattern;
		} );

	return destructuredProps
		.reduce( ( propNames: Set<string>, varDec: VariableDeclaration ) => {
			const destructuredPropNames = parseDestructuredProps( varDec.compilerNode.name as ts.ObjectBindingPattern );
			destructuredPropNames.forEach( propName => propNames.add( propName ) );

			return propNames;
		}, new Set<string>() );
}


/**
 * Parses property accesses of variables that are assigned to the `this`
 * keyword.
 *
 * For example:
 *
 *     var that = this;
 *
 *     that.someProp1 = 1;
 *     that.someProp2 = 2;
 *
 * In the above code, the Set( [ 'someProp1', 'someProp2' ] ) is returned
 */
function parsePropertyAccessesOfThisAssignedVars(
	fileClass: ClassDeclaration
): Set<string> {
	const methods = fileClass.getMethods();

	return methods.reduce( ( propNames: Set<string>, method: MethodDeclaration ) => {
		const thisVarDeclarations = method
			.getDescendantsOfKind( SyntaxKind.VariableDeclaration )
			.filter( isThisReferencingVar );

		// Get the array of identifiers assigned to `this`. Ex: [ 'that', 'self' ]
		const thisVarIdentifiers = thisVarDeclarations
			.map( ( thisVarDec: VariableDeclaration ) => thisVarDec.getName() );

		thisVarIdentifiers.forEach( ( thisVarIdentifier: string ) => {
			// Get the properties accessed from the `this` identifiers (i.e. from
			// 'that', 'self', etc.)
			const propNamesAccessedFromIdentifier = method
				.getDescendantsOfKind( SyntaxKind.PropertyAccessExpression )
				.filter( propertyAccessWithObjFilter( thisVarIdentifier ) )
				.map( ( p: PropertyAccessExpression ) => p.getName() );

			propNamesAccessedFromIdentifier
				.forEach( ( propName: string ) => propNames.add( propName ) );
		} );

		return propNames;
	}, new Set<string>() );
}

================================================
FILE: src/converter/add-class-property-declarations/parse-superclass-name-and-path.ts
================================================
import { isValidIdentifier } from "../../util/is-valid-identifier";
import { ClassDeclaration, SourceFile } from "ts-morph";
import { findImportForIdentifier } from "../../util/find-import-for-identifier";
const resolve = require( 'resolve' );
const TraceError = require( 'trace-error' );

/**
 * Given a file and ClassDeclaration, finds the name of the superclass and the
 * full path to the module (file) that hosts the superclass.
 *
 * `superclass` and `superclassPath` in the return object will be `null` if
 * there is no superclass.
 */
export function parseSuperclassNameAndPath(
	file: SourceFile,
	fileClass: ClassDeclaration
): {
	superclassName: string | undefined;
	superclassPath: string | undefined;
} {
	let superclassName: string | undefined;
	let superclassPath: string | undefined;

	const heritage = fileClass.getExtends();
	if( heritage ) {
		superclassName = heritage.getExpression().getText();

		// Confirm that the superclass is an identifier rather than an
		// expression. It would be a bit much to try to understand expressions
		// as a class's 'extends', so just ignore these for now.
		// Example of ignored class extends:
		//
		//    class MyClass extends Mixin.mix( MixinClass1, MixinClass2 )
		//
		if( !isValidIdentifier( superclassName ) ) {
			superclassName = undefined;  // superclass was not a valid identifier

		} else if( !!file.getClass( superclassName ) ) {
			superclassPath = file.getFilePath();

		} else {
			superclassPath = findImportPathForIdentifier( file, superclassName );
		}
	}

	return {
		superclassName,
		superclassPath: superclassPath && superclassPath.replace( /\\/g, '/' )  // normalize backslashes on Windows to forward slashes so we can compare directories with the paths that ts-morph produces
	};
}


/**
 * Finds the absolute path for the import with the given `identifier`.
 *
 * For example, if we were looking for the identifier 'MyClass' in the following
 * list of imports:
 *
 *     import { Something } from './somewhere';
 *     import { MyClass } from './my-class';
 *
 * Then the method would return '/absolute/path/to/my-class.js';
 *
 * If there is no import for `identifier`, the method returns `undefined`.
 */
function findImportPathForIdentifier(
	sourceFile: SourceFile,
	identifier: string
): string | undefined {
	const importWithIdentifier = findImportForIdentifier( sourceFile, identifier );

	if( importWithIdentifier ) {
		const moduleSpecifier = importWithIdentifier.getModuleSpecifier().getLiteralValue();

		if( !moduleSpecifier.startsWith( '.' ) ) {
			// if the import path isn't relative (i.e. doesn't start with './'
			// or '../'), then it must be in node_modules. Return `undefined` to
			// represent that. We don't want to parse node_modules, and we
			// should be able to migrate the codebase without node_modules even
			// being installed.
			return undefined;
		}

		// If it's a relative import, return the absolute path to the module,
		// based on the source file that the import was found
		const basedir = sourceFile.getDirectoryPath();
		try {
			return resolve.sync( moduleSpecifier, {
				basedir,
				extensions: [ '.ts', '.js' ]
			} );

		} catch( error ) {
			throw new TraceError( `
				An error occurred while trying to resolve the absolute path to
				the import of identifier '${identifier}' in source file:
				    '${sourceFile.getFilePath()}'
				    
				Was looking at the import with text:
				    ${importWithIdentifier.getText()}   
			`.trim().replace( /^\t*/gm, '' ), error );
		}
	}

	// Nothing found, return undefined
	return undefined;
}

================================================
FILE: src/converter/add-optionals-to-function-params.ts
================================================
import { Project, CallExpression, ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, MethodDeclaration, NewExpression, Node, SourceFile, SyntaxKind } from "ts-morph";
import logger from "../logger/logger";

type NameableFunction = FunctionDeclaration | MethodDeclaration;
type FunctionTransformTarget = NameableFunction | ConstructorDeclaration;

/**
 * Adds the question token to function/method/constructor parameters that are
 * deemed to be optional based on the calls to that function/method/constructor
 * in the codebase.
 *
 * For example, if we have:
 *
 *     function myFn( arg1, arg2, arg3 ) {
 *         // ...
 *     }
 *
 *     myFn( 1, 2, 3 );  // all 3 args provided
 *     myFn( 1, 2 );     // <-- a call site only provides two arguments
 *
 * Then the resulting TypeScript function will be:
 *
 *     function myFn( arg1, arg2, arg3? ) {   // <-- arg3 marked as optional
 *         // ...
 *     }
 *
 * Note: Just calling the language service to look up references takes a lot of
 * time. Might have to optimize this somehow in the future.
 */
export function addOptionalsToFunctionParams( tsAstProject: Project ): Project {
	logger.verbose( 'Beginning routine to mark function parameters as optional when calls exist that supply fewer args than parameters...' );
	const sourceFiles = tsAstProject.getSourceFiles();

	logger.verbose( 'Parsing function/method/constructor calls from codebase.' );
	const constructorMinArgsMap = parseClassConstructorCalls( sourceFiles );
	const functionsMinArgsMap = parseFunctionAndMethodCalls( sourceFiles );

	logger.verbose( 'Marking parameters as optional' );
	addOptionals( constructorMinArgsMap );
	addOptionals( functionsMinArgsMap );

	return tsAstProject;
}


/**
 * Finds the call sites of each ClassDeclaration's constructor in order to
 * determine if any of its parameters should be marked as optional.
 *
 * Returns a Map keyed by ClassDeclaration which contains the minimum number of
 * arguments passed to that class's constructor.
 *
 * Actually marking the parameters as optional is done in a separate phase.
 */
function parseClassConstructorCalls( sourceFiles: SourceFile[] ): Map<ConstructorDeclaration, number> {
	logger.verbose( 'Finding all calls to class constructors...' );
	const constructorMinArgsMap = new Map<ConstructorDeclaration, number>();

	sourceFiles.forEach( ( sourceFile: SourceFile ) => {
		logger.verbose( `  Processing classes in source file: ${sourceFile.getFilePath()}` );
		const classes = sourceFile.getDescendantsOfKind( SyntaxKind.ClassDeclaration );

		classes.forEach( ( classDeclaration: ClassDeclaration ) => {
			const constructorFns = classDeclaration.getConstructors() || [];
			const constructorFn = constructorFns.length > 0 ? constructorFns[ 0 ] : undefined;  // only grab the first since we're converting JavaScript

			// If there is no constructor function for this class, then nothing to do
			if( !constructorFn ) {
				return;
			}

			logger.verbose( `    Looking for calls to the constructor of class: '${classDeclaration.getName()}'` );

			const constructorFnParams = constructorFn.getParameters();
			const numParams = constructorFnParams.length;

			const referencedNodes = classDeclaration.findReferencesAsNodes();

			const callsToConstructor = referencedNodes
				.map( ( node: Node ) => node.getFirstAncestorByKind( SyntaxKind.NewExpression ) )
				.filter( ( node ): node is NewExpression => !!node );

			logger.debug( `    Found ${callsToConstructor.length} call(s) to the constructor` );

			const minNumberOfCallArgs = callsToConstructor
				.reduce( ( minCallArgs: number, call: NewExpression ) => {
					return Math.min( minCallArgs, call.getArguments().length );
				}, numParams );

			if( callsToConstructor.length > 0 ) {
				logger.debug( `    Constructor currently expects ${numParams} params. Call(s) to the constructor supply a minimum of ${minNumberOfCallArgs} args.` );
			}

			constructorMinArgsMap.set( constructorFn, minNumberOfCallArgs );
		} );
	} );

	return constructorMinArgsMap;
}


/**
 * Finds the call sites of each FunctionDeclaration or MethodDeclaration in
 * order to determine if any of its parameters should be marked as optional.
 *
 * Returns a Map keyed by FunctionDeclaration or MethodDeclaration which contains
 * the minimum number of arguments passed to that function/method.
 *
 * Actually marking the parameters as optional is done in a separate phase.
 */
function parseFunctionAndMethodCalls( sourceFiles: SourceFile[] ): Map<NameableFunction, number> {
	logger.verbose( 'Finding all calls to functions/methods...' );
	const functionsMinArgsMap = new Map<NameableFunction, number>();

	sourceFiles.forEach( ( sourceFile: SourceFile ) => {
		logger.verbose( `  Processing functions/methods in source file: ${sourceFile.getFilePath()}` );
		const fns = getFunctionsAndMethods( sourceFile );

		fns.forEach( ( fn: NameableFunction ) => {
			logger.verbose( `    Looking for calls to the function: '${fn.getName()}'` );
			const fnParams = fn.getParameters();
			const numParams = fnParams.length;

			const referencedNodes = fn.findReferencesAsNodes();

			const callsToFunction = referencedNodes
				.map( ( node: Node ) => node.getFirstAncestorByKind( SyntaxKind.CallExpression ) )
				.filter( ( node ): node is CallExpression => !!node );

			logger.debug( `    Found ${callsToFunction.length} call(s) to the function '${fn.getName()}'` );

			const minNumberOfCallArgs = callsToFunction
				.reduce( ( minCallArgs: number, call: CallExpression ) => {
					return Math.min( minCallArgs, call.getArguments().length );
				}, numParams );

			if( callsToFunction.length > 0 ) {
				logger.debug( `    Function currently expects ${numParams} params. Call(s) to the function/method supply a minimum of ${minNumberOfCallArgs} args.` );
			}

			functionsMinArgsMap.set( fn, minNumberOfCallArgs );
		} );
	} );

	return functionsMinArgsMap;
}


/**
 * Retrieves all FunctionDeclarations and MethodDeclarations from the given
 * source file.
 */
function getFunctionsAndMethods(
	sourceFile: SourceFile
): NameableFunction[] {
	return ( [] as NameableFunction[] ).concat(
		sourceFile.getDescendantsOfKind( SyntaxKind.FunctionDeclaration ),
		sourceFile.getDescendantsOfKind( SyntaxKind.MethodDeclaration )
	);
}



/**
 * Marks parameters of class constructors / methods / functions as optional
 * based on the minimum number of arguments passed in at its call sites.
 *
 * Ex:
 *
 *     class SomeClass {
 *         constructor( arg1, arg2 ) {}
 *     }
 *     new SomeClass( 1 );  // no arg2
 *
 *     function myFn( arg1, arg2 ) {}
 *     myFn();  // no args
 *
 *
 * Output class and function:
 *
 *     class SomeClass {
 *         constructor( arg1, arg2? ) {}  // <-- arg2 marked as optional
 *     }
 *
 *     function myFn( arg1?, arg2? ) {}   // <-- arg1 and arg2 marked as optional
 */
function addOptionals( minArgsMap: Map<FunctionTransformTarget, number> ) {
	const fns = minArgsMap.keys();

	for( const fn of fns ) {
		const fnParams = fn.getParameters();

		const numParams = fnParams.length;
		const minNumberOfCallArgs = minArgsMap.get( fn )!;

		// Mark all parameters greater than the minNumberOfCallArgs as
		// optional (if it's not a rest parameter or already has a default value)
		for( let i = minNumberOfCallArgs; i < numParams; i++ ) {
			const param = fnParams[ i ];

			if( !param.isRestParameter() && !param.hasInitializer() ) {
				param.setHasQuestionToken( true );
			}
		}
	}
}

================================================
FILE: src/converter/convert.ts
================================================
import { Project, SyntaxKind } from "ts-morph";
import { addClassPropertyDeclarations } from "./add-class-property-declarations/add-class-property-declarations";
import { addOptionalsToFunctionParams } from "./add-optionals-to-function-params";
import { filterOutNodeModules } from "./filter-out-node-modules";
import logger from "../logger/logger";

/**
 * Converts the source .js code to .ts
 */
export function convert( tsAstProject: Project ): Project {
	if( tsAstProject.getSourceFiles().length === 0 ) {
		logger.info( 'Found no source files to process. Exiting.' );
		return tsAstProject;
	}

	// Print input files
	logger.info( 'Processing the following source files:' );
	printSourceFilesList( tsAstProject, '  ' );

	logger.info( `
		Converting source files... This may take anywhere from a few minutes to 
		tens of minutes or longer depending on how many files are being 
		converted.
	`.replace( /\t*/gm, '' ) );

	// Fill in PropertyDeclarations for properties used by ES6 classes
	logger.info( 'Adding property declarations to JS Classes...' );
	tsAstProject = addClassPropertyDeclarations( tsAstProject );

	// Rename .js files to .ts files
	logger.info( 'Renaming .js files to .ts' );
	tsAstProject.getSourceFiles().forEach( sourceFile => {
		const ext = sourceFile.getExtension();

		if( ext === '.js' || ext === '.jsx' ) {
			const dir = sourceFile.getDirectoryPath();
			const basename = sourceFile.getBaseNameWithoutExtension();

			// in case there's a '.js' file which has JSX in it
			const fileHasJsx = sourceFile.getFirstDescendantByKind( SyntaxKind.JsxElement )
				|| sourceFile.getFirstDescendantByKind( SyntaxKind.JsxSelfClosingElement );
			const extension = ( fileHasJsx || ext === '.jsx' ) ? 'tsx' : 'ts';
			const outputFilePath = `${dir}/${basename}.${extension}`;

			logger.debug( `  Renaming ${sourceFile.getFilePath()} to ${outputFilePath}` );
			sourceFile.move( outputFilePath );
		}
	} );

	// Filter out any node_modules files that accidentally got included by an import.
	// We don't want to modify these when we save the project
	tsAstProject = filterOutNodeModules( tsAstProject );

	// Make function parameters optional for calls that supply fewer arguments
	// than there are function parameters.
	// NOTE: Must happen after .js -> .ts rename for the TypeScript Language
	// Service to work.
	logger.info( 'Making parameters optional for calls that supply fewer args than function parameters...' );
	tsAstProject = addOptionalsToFunctionParams( tsAstProject );

	// Filter out any node_modules files as we don't want to modify these when
	// we save the project. Also, some .d.ts files get included for some reason
	// like tslib.d.ts, so we don't want to output that as well.
	tsAstProject = filterOutNodeModules( tsAstProject );

	// Print output files
	logger.info( 'Outputting .ts files:' );
	printSourceFilesList( tsAstProject, '  ' );

	// Even though the `tsAstProject` has been mutated (it is not an immutable
	// data structure), return it anyway to avoid the confusion of an output
	// parameter.
	return tsAstProject;
}


/**
 * Private helper to print out the source files list in the given `astProject`
 * to the console.
 */
function printSourceFilesList( astProject: Project, indent = '' ) {
	astProject.getSourceFiles().forEach( sf => {
		logger.info( `${indent}${sf.getFilePath()}` );
	} );
}

================================================
FILE: src/converter/filter-out-node-modules.ts
================================================
import { Project } from "ts-morph";

/**
 * Given a Project, removes all files that are under the node_modules folder.
 *
 * It seems the language service can pull in some .d.ts files from node_modules
 * that we don't want to be output after we save.
 */
export function filterOutNodeModules( tsAstProject: Project ): Project {
	tsAstProject.getSourceFiles().forEach( sourceFile => {
		if( sourceFile.getFilePath().includes( 'node_modules' ) ) {
			tsAstProject.removeSourceFile( sourceFile );
		}
	} );

	return tsAstProject;
}

================================================
FILE: src/create-ts-morph-project.ts
================================================
import { Project, IndentationText } from "ts-morph";
import fastGlob from 'fast-glob';

/**
 * Creates a ts-morph Project by including the source files under the given
 * `directory`.
 *
 * @param directory The absolute path to the directory of .js files to
 *   include.
 * @param options
 * @param options.indentationText The text used to indent new class property
 *   declarations.
 * @param options.excludePatterns Glob patterns to exclude files.
 */
export function createTsMorphProject( directory: string, options: {
	indentationText?: IndentationText,
	includePatterns?: string[],
	excludePatterns?: string[]
} = {} ) {
	const tsMorphProject = new Project( {
		manipulationSettings: {
			indentationText: options.indentationText || IndentationText.Tab
		}
	} );

	// Read files using fast-glob. fast-glob does a much better job over node-glob
	// at ignoring directories like node_modules without reading all of the files 
	// in them first
	let files = fastGlob.sync( options.includePatterns || `**/*.+(js|ts|jsx|tsx)`, {
		cwd: directory,
		absolute: true,
		followSymbolicLinks: true,

		// filter out any path which includes node_modules. We don't want to
		// attempt to parse those as they may be ES5, and we also don't accidentally
		// want to write out into the node_modules folder
		ignore: ['**/node_modules/**'].concat(options.excludePatterns || [])
	} );

	files.forEach( ( filePath: string ) => {
		tsMorphProject.addSourceFileAtPath( filePath )
	} );

	return tsMorphProject;
}



================================================
FILE: src/index.ts
================================================
export * from './js-to-ts-converter';
export * from './logger/log-level';

================================================
FILE: src/js-to-ts-converter.ts
================================================
import * as path from 'path';
import { createTsMorphProject } from "./create-ts-morph-project";
import { convert } from "./converter/convert";
import { Project, IndentationText } from "ts-morph";
import { LogLevel } from "./logger";
import logger from "./logger/logger";

export interface JsToTsConverterOptions {
	indentationText?: IndentationText,
	logLevel?: LogLevel,
	includePatterns?: string[],
	excludePatterns?: string[]
}

/**
 * Asynchronously converts the JavaScript files under the given `sourceFilesPath`
 * to TypeScript files.
 *
 * @param sourceFilesPath The path to the source files to convert
 * @param [options]
 * @param [options.indentationText] The text used to indent new class property
 *   declarations.
 * @param [options.logLevel] The level of logging to show on the console.
 *   One of: 'debug', 'verbose', 'info', 'warn', 'error'
 * @param [options.includePatterns] Glob patterns to include files.
 * @param [options.excludePatterns] Glob patterns to exclude files.
 */
export async function convertJsToTs(
	sourceFilesPath: string,
	options: JsToTsConverterOptions = {}
): Promise<void> {
	const convertedTsAstProject = doConvert( sourceFilesPath, options );

	// Save output files
	return convertedTsAstProject.save();
}

/**
 * Synchronously converts the JavaScript files under the given `sourceFilesPath`
 * to TypeScript files.
 *
 * @param sourceFilesPath The path to the source files to convert
 * @param [options]
 * @param [options.indentationText] The text used to indent new class property
 *   declarations.
 * @param [options.logLevel] The level of logging to show on the console.
 *   One of: 'debug', 'verbose', 'info', 'warn', 'error'
 * @param [options.includePatterns] Glob patterns to include files.
 * @param [options.excludePatterns] Glob patterns to exclude files.
 */
export function convertJsToTsSync(
	sourceFilesPath: string,
	options: JsToTsConverterOptions = {}
) {
	const convertedTsAstProject = doConvert( sourceFilesPath, options );

	// Save output files
	convertedTsAstProject.saveSync();
}


/**
 * Performs the actual conversion given a `sourceFilesPath`, and returning a
 * `ts-morph` Project with the converted source files.
 *
 * @param sourceFilesPath The path to the source files to convert
 * @param [options]
 * @param [options.indentationText] The text used to indent new class property
 *   declarations.
 * @param [options.logLevel] The level of logging to show on the console.
 *   One of: 'debug', 'verbose', 'info', 'warn', 'error'
 * @param [options.includePatterns] Glob patterns to include files.
 * @param [options.excludePatterns] Glob patterns to exclude files.
 */
function doConvert(
	sourceFilesPath: string,
	options: JsToTsConverterOptions = {}
): Project {
	logger.setLogLevel( options.logLevel || 'verbose' );

	const absolutePath = path.resolve( sourceFilesPath );

	const tsAstProject = createTsMorphProject( absolutePath, options );
	return convert( tsAstProject );
}

================================================
FILE: src/logger/index.ts
================================================
export * from './logger';
export * from './log-level';

import logger from './logger';
export default logger;

================================================
FILE: src/logger/log-level.ts
================================================
export type LogLevel = 'debug' | 'verbose' | 'info' | 'warn' | 'error';
export const logLevels = [ 'debug', 'verbose', 'info', 'warn', 'error' ];


================================================
FILE: src/logger/logger.ts
================================================
import * as winston from 'winston';
import { LogLevel } from "./log-level";

const winstonLogger = winston.createLogger( {
	level: 'verbose',  // may be changed by Logger.setLogLevel()
	transports: [
		new winston.transports.Console( {
			format: winston.format.combine(
				winston.format.colorize(),
				winston.format.align(),
				winston.format.printf(info => `${info.level}: ${info.message}`)
			)
		} )
	]
} );


/**
 * Abstraction layer for the Winston logger. The methods are in order from
 * highest level of logging to lowest.
 */
class Logger {

	setLogLevel( logLevel: LogLevel ) {
		winstonLogger.level = logLevel;
	}

	debug( message: string ) {
		winstonLogger.log( 'debug', message );
	}

	verbose( message: string ) {
		winstonLogger.log( 'verbose', message );
	}

	info( message: string ) {
		winstonLogger.log( 'info', message );
	}

	log( message: string ) {
		winstonLogger.log( 'info', message );
	}

	warn( message: string ) {
		winstonLogger.log( 'warn', message );
	}

	error( message: string ) {
		winstonLogger.log( 'error', message );
	}

}

const logger = new Logger();
export default logger;

================================================
FILE: src/util/find-import-for-identifier.ts
================================================
import { ImportDeclaration, ImportSpecifier, SourceFile } from "ts-morph";

/**
 * Finds an ImportDeclaration for a given identifier (name).
 *
 * For instance, given this source file:
 *
 *     import { SomeClass1, SomeClass2 } from './somewhere';
 *     import { SomeClass3 } from './somewhere-else';
 *
 *     // ...
 *
 * And a call such as:
 *
 *     findImportForIdentifier( sourceFile, 'SomeClass3' );
 *
 * Then the second ImportDeclaration will be returned.
 */
export function findImportForIdentifier(
	sourceFile: SourceFile,
	identifier: string
): ImportDeclaration | undefined {
	return sourceFile
		.getImportDeclarations()
		.find( ( importDeclaration: ImportDeclaration ) => {
			const hasNamedImport = importDeclaration.getNamedImports()
				.map( ( namedImport: ImportSpecifier ) => namedImport.getName() )
				.includes( identifier );

			const defaultImport = importDeclaration.getDefaultImport();
			const hasDefaultImport = !!defaultImport && defaultImport.getText() === identifier;

			return hasNamedImport || hasDefaultImport;
		} );
}

================================================
FILE: src/util/is-element-access-with-obj.ts
================================================
import { ElementAccessExpression, Identifier, Node } from "ts-morph";

/**
 * Determines if the given `node` is a ElementAccessExpression whose object is
 * `obj`.
 *
 * Example, in the following expression:
 *
 *     obj['a']
 *
 * This function will return true if called as:
 *
 *     isElementAccessWithObj( expr, 'obj' );
 */
export function isElementAccessWithObj(
	node: Node,
	objIdentifier: string
): node is ElementAccessExpression {
	if( !Node.isElementAccessExpression( node ) ) {
		return false;
	}

	const expr = node.getExpression();

	if( objIdentifier === 'this' ) {
		return Node.isThisExpression( expr );

	} else if( Node.isIdentifier( expr ) ) {
		const identifier = expr as Identifier;

		return identifier.getText() === objIdentifier;

	} else {
		return false;
	}
}

/**
 * Function intended to be used with Array.prototype.filter() to return any
 * ElementAccessExpression that uses the object `obj`.
 *
 * For example, in this source code:
 *
 *     const obj = { a: 1, b: 2 };
 *     obj['a'] = 3;
 *
 *     const obj2 = { a: 3, b: 4 };
 *     obj2['b'] = 5;
 *
 * We can use the following to find the 'obj2' element access:
 *
 *     const propAccesses = sourceFile
 *         .getDescendantsOfKind( SyntaxKind.ElementAccessExpression );
 *
 *     const obj2PropAccesses = propAccesses
 *         .filter( elementAccessWithObjFilter( 'obj2' ) );
 */
export function elementAccessWithObjFilter( objIdentifier: string ): ( node: Node ) => node is ElementAccessExpression {
	return ( node: Node ): node is ElementAccessExpression => {
		return isElementAccessWithObj( node, objIdentifier );
	};
}

================================================
FILE: src/util/is-property-access-with-obj.ts
================================================
import { Identifier, Node, PropertyAccessExpression } from "ts-morph";

/**
 * Determines if the given `node` is a PropertyAccessExpression or
 * ElementAccessExpression whose object is `obj`.
 *
 * Example, in the following expression:
 *
 *     obj.a
 *
 * This function will return true if called as:
 *
 *     isPropertyOrElemementAccessWithObj( expr, 'obj' );
 */
export function isPropertyAccessWithObj(
	node: Node,
	objIdentifier: string
): node is PropertyAccessExpression {
	if( !Node.isPropertyAccessExpression( node ) ) {
		return false;
	}

	const expr = node.getExpression();

	if( objIdentifier === 'this' ) {
		return Node.isThisExpression( expr );

	} else if( Node.isIdentifier( expr ) ) {
		const identifier = expr as Identifier;

		return identifier.getText() === objIdentifier;

	} else {
		return false;
	}
}

/**
 * Function intended to be used with Array.prototype.filter() to return any
 * PropertyAccessExpression that uses the object `obj`.
 *
 * For example, in this source code:
 *
 *     const obj = { a: 1, b: 2 };
 *     obj.a = 3;
 *
 *     const obj2 = { a: 3, b: 4 };
 *     obj2.b = 5;
 *
 * We can use the following to find the 'obj2' property access:
 *
 *     const propAccesses = sourceFile
 *         .getDescendantsOfKind( SyntaxKind.PropertyAccessExpression );
 *
 *     const obj2PropAccesses = propAccesses
 *         .filter( propAccessWithObjFilter( 'obj2' ) );
 */
export function propertyAccessWithObjFilter( objIdentifier: string ): ( node: Node ) => node is PropertyAccessExpression {
	return ( node: Node ): node is PropertyAccessExpression => {
		return isPropertyAccessWithObj( node, objIdentifier );
	};
}

================================================
FILE: src/util/is-property-or-elemement-access-with-obj.ts
================================================
import { ElementAccessExpression, Node, PropertyAccessExpression } from "ts-morph";
import { isPropertyAccessWithObj } from "./is-property-access-with-obj";
import { isElementAccessWithObj } from "./is-element-access-with-obj";

/**
 * Determines if the given `node` is a PropertyAccessExpression or
 * ElementAccessExpression whose object is `obj`.
 *
 * Example, in the following expression:
 *
 *     obj.a
 *
 * This function will return true if called as:
 *
 *     isPropertyOrElemementAccessWithObj( expr, 'obj' );
 */
export function isPropertyOrElemementAccessWithObj(
	node: Node,
	objIdentifier: string
): node is PropertyAccessExpression | ElementAccessExpression {
	return isPropertyAccessWithObj( node, objIdentifier )
		|| isElementAccessWithObj( node, objIdentifier );
}

/**
 * Function intended to be used with Array.prototype.filter() to return any
 * PropertyAccessExpression or ElementAccessExpression that uses the object
 * `obj`.
 *
 * For example, in this source code:
 *
 *     const obj = { a: 1, b: 2 };
 *     obj.a = 3;
 *     obj['b'] = 4;
 *
 *     const obj2 = { a: 3, b: 4 };
 *     obj2.a = 5;
 *     obj2['b'] = 6;
 *
 * We can use the following to find the two 'obj2' property accesses:
 *
 *     const propOrElementAccesses = sourceFile
 *         .getDescendantsOfKind( SyntaxKind.PropertyAccessExpression )
 *         .concat( sourceFile
 *             .getDescendantsOfKind( SyntaxKind.ElementAccessExpression )
 *         );
 *
 *     const obj2PropOrElemAccesses = propOrElementAccesses
 *         .filter( propertyOrElementAccessWithObjFilter( 'obj2' ) );
 */
export function propertyOrElementAccessWithObjFilter( objIdentifier: string ): ( node: Node ) => node is PropertyAccessExpression | ElementAccessExpression {
	return ( node: Node ): node is PropertyAccessExpression | ElementAccessExpression => {
		return isPropertyOrElemementAccessWithObj( node, objIdentifier );
	};
}

================================================
FILE: src/util/is-this-referencing-var.ts
================================================
import { Node, SyntaxKind, VariableDeclaration } from "ts-morph";

/**
 * Determines if the given AST Node is a VariableDeclaration of the form:
 *
 *     var self = this;
 *
 *
 * Will return false for the following, however, since this is a destructuring
 * of the `this` object's properties.
 *
 *     var { prop1, prop2 } = this;
 */
export function isThisReferencingVar( node: Node ): node is VariableDeclaration {
	if( !Node.isVariableDeclaration( node ) ) {
		return false;
	}

	const varDec = node as VariableDeclaration;

	const initializerIsThisKeyword = !!varDec.getInitializerIfKind( SyntaxKind.ThisKeyword );
	const assignedToSingleIdentifier = varDec.compilerNode.name.kind === SyntaxKind.Identifier;

	return initializerIsThisKeyword && assignedToSingleIdentifier;
}

================================================
FILE: src/util/is-valid-identifier.ts
================================================
/**
 * Helper to determine if a string of text is a valid JavaScript identifier.
 */
export function isValidIdentifier( text: string ) {
	return /^[\w$]+$/.test( text );
}

================================================
FILE: src/util/parse-destructured-props.ts
================================================
import { ts, SyntaxKind } from "ts-morph";

/**
 * Given a ts.ObjectBindingPattern node, returns an array of the names that
 * are bound to it.
 *
 * These names are essentially the property names pulled out of the object.
 *
 * Example:
 *
 *     var { a, b } = this;
 *
 * Returns:
 *
 *     [ 'a', 'b' ]
 */
export function parseDestructuredProps( node: ts.ObjectBindingPattern ): string[] {
	const elements = node.elements;

	return elements
		.filter( ( element: ts.BindingElement ) => {
			return element.name.kind === SyntaxKind.Identifier;
		} )
		.map( ( element: ts.BindingElement ) => {
			return ( element.name as ts.Identifier ).text;
		} );
}

================================================
FILE: src/util/set-utils.ts
================================================
/**
 * Unions two or more sets to create a combined set. Does not mutate the input
 * sets.
 */
export function union<T>( setA: Set<T>, ...sets: Set<T>[] ) {
	const union = new Set<T>( setA );

	sets.forEach( currentSet => {
		for( const elem of currentSet ) {
			union.add( elem );
		}
	} );
	return union;
}


/**
 * Removes the elements of `setB` from `setA` to produce the difference. Does
 * not mutate the input sets.
 */
export function difference<T>( setA: Set<T>, setB: Set<T> ) {
	const difference = new Set( setA );
	for( const elem of setB ) {
		difference.delete( elem );
	}
	return difference;
}

================================================
FILE: test/convert.spec.ts
================================================
import { expect } from 'chai';
import { createTsMorphProject } from "../src/create-ts-morph-project";
import { convert } from "../src/converter/convert";
import { SourceFile } from "ts-morph";
import * as fs from "fs";
import logger from "../src/logger/logger";
import { JsToTsConverterOptions } from "../src";

// Minimal logging for tests
logger.setLogLevel( 'error' );

describe( 'convert()', () => {

	it( `should convert JS classes to TS-compilable classes by filling in field
	     (property) declarations for properties consumed in the original JS 
	     classes`,
	() => {
		runTest( `${__dirname}/fixture/superclass-subclass` );
	} );


	it( `should ignore expressions (i.e. non-identifiers) in the 'extends' 
	     clause of a class (at least for the moment, this would be too much
	     to parse and figure out - may support in the future)`,
	() => {
		runTest( `${__dirname}/fixture/expression-extends` );
	} );


	it( `should not fill in property declarations for properties that are already
	     declared (such as if the utility is run against a typescript codebase),
	     but should fill in any missing properties that are not declared`,
	() => {
		runTest( `${__dirname}/fixture/typescript-class` );
	} );


	it( `should handle 'var this = that' by adding 'that.xyz' as a class
	     property declaration`,
	() => {
		runTest( `${__dirname}/fixture/function-expressions-and-declarations` );
	} );


	it( `should make function parameters optional when call sites are found to
	     supply fewer arguments than there are parameters`,
	() => {
		runTest( `${__dirname}/fixture/function-calls-with-fewer-args-than-params` );
	} );


	it( `should not require node_modules to be installed in order to convert a
	     codebase`,
	() => {
		runTest( `${__dirname}/fixture/superclass-subclass-node-modules-not-installed` );
	} );


	it( `should properly handle includePatterns and excludePatterns options`, () => {
		runTest( `${__dirname}/fixture/include-exclude-patterns`, {
			includePatterns: [ '**/included/**' ],
			excludePatterns: [ '**/included/excluded/**' ]
		} );
	} );

	it( `should properly convert a React .jsx file to .tsx`, () => {
		runTest( `${__dirname}/fixture/react-class-jsx` );
	} );

	it( `should properly convert a React .js file which has JSX within it
		 to .tsx`, 
	() => {
		runTest( `${__dirname}/fixture/react-class-js` );
	} );

	it( `should properly convert a React .js file which has only a self-closing JSX
		 tag within it to .tsx (https://github.com/gregjacobs/js-to-ts-converter/issues/15),
		 and also not error with a self-closing JSX element (https://github.com/gregjacobs/js-to-ts-converter/issues/4)`,
	() => {
		runTest( `${__dirname}/fixture/react-jsx-self-closing-element` );
	} );

	it( `should not do anything with a reference to this.constructor (https://github.com/gregjacobs/js-to-ts-converter/issues/9)`,
	() => {
		runTest( `${__dirname}/fixture/class-with-this-constructor-reference` );
	} );

} );


/**
 * Runs a test of the conversion utility by passing it a directory that has
 * two subdirectories:
 *
 * - input
 * - expected
 *
 * The `input` directory will be converted, and then compared to the
 * `expected` directory.
 *
 * @param absolutePath Absolute path to the directory which has
 *   `input` and `expected` subdirectories.
 * @param [inputFilesOptions] The options to configure the converter.
 */
function runTest(
	absolutePath: string,
	inputFilesOptions?: JsToTsConverterOptions
) {
	if( !fs.lstatSync( absolutePath ).isDirectory() ) {
		throw new Error( 'The absolute path: ' + absolutePath + ' is not a directory' );
	}
	if( !fs.lstatSync( absolutePath + '/input' ).isDirectory() ) {
		throw new Error( 'The absolute path: ' + absolutePath + '/input is not a directory' );
	}
	if( !fs.lstatSync( absolutePath + '/expected' ).isDirectory() ) {
		throw new Error( 'The absolute path: ' + absolutePath + '/expected is not a directory' );
	}

	const inputFilesProject = createTsMorphProject( absolutePath + '/input', inputFilesOptions );
	const expectedFilesProject = createTsMorphProject( absolutePath + '/expected' );

	if( inputFilesProject.getSourceFiles().length === 0 ) {
		throw new Error( `No source files were found in the input directory: ${absolutePath}/input` );
	}

	const convertedInputProject = convert( inputFilesProject );

	const convertedSourceFiles = convertedInputProject.getSourceFiles();
	const expectedSourceFiles = expectedFilesProject.getSourceFiles();
	const convertedSourceFilePaths = convertedInputProject.getSourceFiles().map( sf => sf.getFilePath() );
	const expectedSourceFilePaths = expectedFilesProject.getSourceFiles().map( sf => sf.getFilePath() );

	// First, make sure that there are the same number of files in the converted
	// and expected projects
	if( convertedSourceFiles.length !== expectedSourceFiles.length ) {
		throw new Error( `
			The number of converted source files (${convertedSourceFiles.length})
			does not match the number of expected source files (${expectedSourceFiles.length}).
			
			Converted source files:
			  ${convertedSourceFilePaths.join( '\n  ' )}
			  
			Expected source files:
			  ${expectedSourceFilePaths.join( '\n  ' )}
		`.replace( /^\t*/gm, '' ) )
	}

	// Now check each converted source file against the expected output file
	convertedSourceFiles.forEach( ( convertedSourceFile: SourceFile ) => {
		const expectedSourceFilePath = convertedSourceFile.getFilePath().replace( /([\\\/])input[\\\/]/, '$1expected$1' );
		const expectedSourceFile = expectedFilesProject.getSourceFile( expectedSourceFilePath );

		if( !expectedSourceFile ) {
			throw new Error( `
				The converted source file (below) does not have a matching 'expected' file: 
				  '${convertedSourceFile.getFilePath()}'
				  
				Tried to find matching expected file: 
				  '${expectedSourceFilePath}'
			`.replace( /^\t*/gm, '' ) );
		}

		expect( convertedSourceFile.getFullText() )
			.to.equal( expectedSourceFile!.getFullText() );
	} );

}


================================================
FILE: test/fixture/class-with-this-constructor-reference/expected/my-class.ts
================================================
export class MyClass {
	public name: any;

	constructor() {}

	myMethod() {
		this.name = this.constructor.name;
	}

}

================================================
FILE: test/fixture/class-with-this-constructor-reference/input/my-class.js
================================================
export class MyClass {

	constructor() {}

	myMethod() {
		this.name = this.constructor.name;
	}

}

================================================
FILE: test/fixture/expression-extends/expected/expression-extends.ts
================================================
class ExpressionExtends extends Mixin.mix( SomeClass1, SomeClass2 ) {
	public someProp: any;

	constructor() {
		this.someProp = 1;
	}
}

================================================
FILE: test/fixture/expression-extends/input/expression-extends.js
================================================
class ExpressionExtends extends Mixin.mix( SomeClass1, SomeClass2 ) {
	constructor() {
		this.someProp = 1;
	}
}

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-exported-function.ts
================================================
import { myExportedFunction } from "./exported-function";

myExportedFunction();  // no args - should mark all as optional

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-local-function-with-default-value.ts
================================================
function threeArg( arg1, arg2 = 1, arg3 = 2 ) {

}

threeArg( 1 );

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-local-function.ts
================================================
function threeArg( arg1, arg2?, arg3? ) {

}

threeArg( 1 );

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-sub-class-method.ts
================================================
import { SubClass } from "./sub-class";

const subClass = new SubClass();

subClass.subclassMethod( 1, 2 );  // should *not* mark any args as optional

subClass.subclassMethod2();  // should mark both arg1 and arg2 as optional


================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-super-class-method.ts
================================================
import { SuperClass } from "./super-class";

const superclass = new SuperClass( 1 );  // marks arg2 and arg3 as optional

superclass.somePublicMethod( 1 );  // marks arg2 as optional


================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/constructor-with-rest-param.ts
================================================
class ConstructorWithRestParam {

	constructor( ...args ) {  // should *not* be marked as optional

	}

	methodWithRestParam( ...args ) {}  // should *not* be marked as optional

}


const instance = new ConstructorWithRestParam();
instance.methodWithRestParam();

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/exported-function.ts
================================================
export function myExportedFunction( arg1?, arg2? ) {  // arg1 and arg2 made optional by call-to-exported-function.js

}

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/sub-class.ts
================================================
import { SuperClass } from "./super-class";

export class SubClass extends SuperClass {

	subclassMethod( arg1, arg2 ) {  // these should *not* be made optional by the call in call-to-sub-class-method.js
		// call superclass method
		this.superclassMethod();  // marks the arg as optional
	}


	subclassMethod2( arg1?, arg2? ) {  // these should both be made optional by the call in call-to-sub-class-method.js

	}

}

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/super-class.ts
================================================
export class SuperClass {

	constructor( arg1, arg2?, arg3? ) {  // arg2 and arg3 will be marked optional by call-to-superclass-method.js

	}


	superclassMethod( arg? ) {  // arg will be marked optional by sub-class.js

	}


	somePublicMethod( arg1, arg2? ) {  // arg2 will be marked optional by call-to-class-method.js

	}

}

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/call-to-exported-function.js
================================================
import { myExportedFunction } from "./exported-function";

myExportedFunction();  // no args - should mark all as optional

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/call-to-local-function-with-default-value.js
================================================
function threeArg( arg1, arg2 = 1, arg3 = 2 ) {

}

threeArg( 1 );

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/call-to-local-function.js
================================================
function threeArg( arg1, arg2, arg3 ) {

}

threeArg( 1 );

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/call-to-sub-class-method.js
================================================
import { SubClass } from "./sub-class";

const subClass = new SubClass();

subClass.subclassMethod( 1, 2 );  // should *not* mark any args as optional

subClass.subclassMethod2();  // should mark both arg1 and arg2 as optional


================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/call-to-super-class-method.js
================================================
import { SuperClass } from "./super-class";

const superclass = new SuperClass( 1 );  // marks arg2 and arg3 as optional

superclass.somePublicMethod( 1 );  // marks arg2 as optional


================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/constructor-with-rest-param.js
================================================
class ConstructorWithRestParam {

	constructor( ...args ) {  // should *not* be marked as optional

	}

	methodWithRestParam( ...args ) {}  // should *not* be marked as optional

}


const instance = new ConstructorWithRestParam();
instance.methodWithRestParam();

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/exported-function.js
================================================
export function myExportedFunction( arg1, arg2 ) {  // arg1 and arg2 made optional by call-to-exported-function.js

}

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/sub-class.js
================================================
import { SuperClass } from "./super-class";

export class SubClass extends SuperClass {

	subclassMethod( arg1, arg2 ) {  // these should *not* be made optional by the call in call-to-sub-class-method.js
		// call superclass method
		this.superclassMethod();  // marks the arg as optional
	}


	subclassMethod2( arg1, arg2 ) {  // these should both be made optional by the call in call-to-sub-class-method.js

	}

}

================================================
FILE: test/fixture/function-calls-with-fewer-args-than-params/input/super-class.js
================================================
export class SuperClass {

	constructor( arg1, arg2, arg3 ) {  // arg2 and arg3 will be marked optional by call-to-superclass-method.js

	}


	superclassMethod( arg ) {  // arg will be marked optional by sub-class.js

	}


	somePublicMethod( arg1, arg2 ) {  // arg2 will be marked optional by call-to-class-method.js

	}

}

================================================
FILE: test/fixture/function-expressions-and-declarations/expected/class-with-function-expressions.ts
================================================
class ClassWithFunctionExpressions {
	public destructured1: any;
	public destructured2: any;
	public prop1: any;
	public prop2: any;
	public innerAccessedProp: any;
	public blah: any;

	myMethod() {
		var that = this;

		var myFn1 = function() {
			that.prop1 = 1;
		}

		var myFn2 = function(a, b) {
			that.prop2 = 1;
			that['prop3'] = 2;
		}
	}

	myMethod2() {
		var self = this,
		    somethingElse = 1;

		var myFn1 = function() {
			self.prop1 = 1;

			var myNestedFn = function() {
				self.innerAccessedProp = 2;
			}
		}
	}

	myMethod3() {
		var somethingElse = 1,
		    me = this;

		var myFn1 = function() {
			me.prop1 = 1;
		}
	}

	destructuredThis() {
		// should simply not throw an error on this construct, while populating
		// these variables as PropertyDeclarations
		const { destructured1, destructured2 } = this;
	}

	complexMethodWhichCausesErrorInTsSimpleAstTransforms() {
		const that = this;

		that.blah.blah2.blah3 = 42;
		that.blah.blah2.blah3.blah4 = 43;

		// below is potentially another test to check, but above seems to
		// display the previous bug
		//
		// if( this.asdf ) {
		// 	_.someFn( that.asdf.asdf2, () => {
		// 		_.someOtherFn( that.blah.blah2.blah3, () => {
		// 		} );
		// } );
		// }

		// if( !this.something ) {
		// 	this.something = this.someOtherThing.fn( () => {
		// 		const abc = [];
		//
		// 		_.forEach(that.model.something.else, (a) => {
		// 			// if( asdf ) {
		// 			// 	that.model.something = 1;
		// 			// } else {
		// 			// 	that.model.somethingElse = 2;
		// 			// }
		// 		} );
		//
		// 		that.model.something = 42;
		// 		_.set( that.model.something, 'abc', 'def' );
		// 		that.somethingElse = 11;
		// 	} );
		// }
	}

}

================================================
FILE: test/fixture/function-expressions-and-declarations/input/class-with-function-expressions.js
================================================
class ClassWithFunctionExpressions {

	myMethod() {
		var that = this;

		var myFn1 = function() {
			that.prop1 = 1;
		}

		var myFn2 = function(a, b) {
			that.prop2 = 1;
			that['prop3'] = 2;
		}
	}

	myMethod2() {
		var self = this,
		    somethingElse = 1;

		var myFn1 = function() {
			self.prop1 = 1;

			var myNestedFn = function() {
				self.innerAccessedProp = 2;
			}
		}
	}

	myMethod3() {
		var somethingElse = 1,
		    me = this;

		var myFn1 = function() {
			me.prop1 = 1;
		}
	}

	destructuredThis() {
		// should simply not throw an error on this construct, while populating
		// these variables as PropertyDeclarations
		const { destructured1, destructured2 } = this;
	}

	complexMethodWhichCausesErrorInTsSimpleAstTransforms() {
		const that = this;

		that.blah.blah2.blah3 = 42;
		that.blah.blah2.blah3.blah4 = 43;

		// below is potentially another test to check, but above seems to
		// display the previous bug
		//
		// if( this.asdf ) {
		// 	_.someFn( that.asdf.asdf2, () => {
		// 		_.someOtherFn( that.blah.blah2.blah3, () => {
		// 		} );
		// } );
		// }

		// if( !this.something ) {
		// 	this.something = this.someOtherThing.fn( () => {
		// 		const abc = [];
		//
		// 		_.forEach(that.model.something.else, (a) => {
		// 			// if( asdf ) {
		// 			// 	that.model.something = 1;
		// 			// } else {
		// 			// 	that.model.somethingElse = 2;
		// 			// }
		// 		} );
		//
		// 		that.model.something = 42;
		// 		_.set( that.model.something, 'abc', 'def' );
		// 		that.somethingElse = 11;
		// 	} );
		// }
	}

}

================================================
FILE: test/fixture/include-exclude-patterns/expected/included/included-file.ts
================================================
class IncludedFile {
	public someProp: any;

	constructor() {
		this.someProp = 1;
	}
}

================================================
FILE: test/fixture/include-exclude-patterns/input/included/excluded/excluded-file.js
================================================
class ExcludedFile {
	constructor() {
		this.someProp = 1;
	}
}

================================================
FILE: test/fixture/include-exclude-patterns/input/included/included-file.js
================================================
class IncludedFile {
	constructor() {
		this.someProp = 1;
	}
}

================================================
FILE: test/fixture/include-exclude-patterns/input/other-file-that-should-not-be-included.js
================================================
class OtherFileThatShouldNotBeIncluded {}

================================================
FILE: test/fixture/react-class-js/expected/react-class.tsx
================================================
import * as React from "react";
import PropTypes from "prop-types";
import {
  TableHead, TableRow, TableCell,
  TableSortLabel, Checkbox
} from "@material-ui/core";
import { Draggable } from "react-beautiful-dnd";

class TableHeader extends React.Component {
	public props: any;

  renderHeader() {
    const mapArr = this.props.columns.filter(columnDef => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1))
      .map((columnDef, index) => (
        <TableCell
          key={columnDef.tableData.id}
          align={["numeric"].indexOf(columnDef.type) !== -1 ? "right" : "left"}

          style={{ ...this.props.headerStyle, ...columnDef.headerStyle }}
        >
          <div className="displayFlex">
            <div>
              {(columnDef.sort !== false && columnDef.sorting !== false && this.props.sorting)
                ? <TableSortLabel
                  active={this.props.orderBy === columnDef.tableData.id}
                  direction={this.props.orderDirection || "asc"}
                  onClick={() => {
                    const orderDirection = columnDef.tableData.id !== this.props.orderBy ? "asc" : this.props.orderDirection === "asc" ? "desc" : "asc";
                    this.props.onOrderChange(columnDef.tableData.id, orderDirection);
                  }}
                >
                  {(this.props.grouping && columnDef.field)
                    ? <Draggable
                      key={columnDef.tableData.id}
                      draggableId={columnDef.tableData.id.toString()}
                      index={index}>
                      {(provided) => (
                        <div
                          ref={provided.innerRef}
                          {...provided.draggableProps}
                          {...provided.dragHandleProps}                          
                        >
                          {columnDef.title}
                        </div>
                      )}
                    </Draggable>
                    : columnDef.title
                  }
                </TableSortLabel>
                : columnDef.title
              }
            </div>
            {columnDef.filter && 
                    <div>
                      {columnDef.filter}
                    </div>

            }
          </div>
        </TableCell>
      ));
    return mapArr;
  }

  renderActionsHeader() {
    const localization = { ...TableHeader.defaultProps.localization, ...this.props.localization };
    return (
      <TableCell
        key="key-actions-column"
        style={this.props.headerStyle}
      >
        <TableSortLabel>{localization.actions}</TableSortLabel>
      </TableCell>
    );
  }
  renderSelectionHeader() {
    return (
      <TableCell
        padding="none"
        key="key-selection-column"
        style={this.props.headerStyle}
      >
        <Checkbox
          indeterminate={this.props.selectedCount > 0 && this.props.selectedCount < this.props.dataCount}
          checked={this.props.selectedCount === this.props.dataCount}
          onChange={(event, checked) => this.props.onAllSelected && this.props.onAllSelected(checked)}
        />
      </TableCell>
    );
  }
  render() {
    const headers = this.renderHeader();
    if (this.props.hasSelection && this.props.dataCount) {
      headers.splice(0, 0, this.renderSelectionHeader());
    }

    if (this.props.showActionsColumn) {
      if (this.props.actionsHeaderIndex >= 0) {
        let endPos = 0;
        if (this.props.hasSelection) {
          endPos = 1;
        }
        headers.splice(this.props.actionsHeaderIndex + endPos, 0, this.renderActionsHeader());
      } else if (this.props.actionsHeaderIndex === -1) {
        headers.push(this.renderActionsHeader());
      }
    }

    if (this.props.hasDetailPanel) {
      headers.splice(0, 0,
        <TableCell
          padding="none"
          key="key-detail-panel-column"
          style={this.props.headerStyle}
        />
      );
    }

    this.props.columns
      .filter(columnDef => columnDef.tableData.groupOrder > -1)
      .forEach(columnDef => {
        headers.splice(0, 0, <TableCell padding="checkbox" key={"key-group-header" + columnDef.tableData.id} />);
      });

    return (
      <TableHead>
        <TableRow>
          {headers}
        </TableRow>
      </TableHead>
    );
  }
}

TableHeader.defaultProps = {
  dataCount: 0,
  hasSelection: false,
  headerStyle: {},
  selectedCount: 0,
  sorting: true,
  localization: {
    actions: "Actions" 
  },
  orderBy: undefined,
  orderDirection: "asc",
  actionsHeaderIndex: 0
};

TableHeader.propTypes = {
  columns: PropTypes.array.isRequired,
  dataCount: PropTypes.number,
  hasDetailPanel: PropTypes.bool.isRequired,
  hasSelection: PropTypes.bool,
  headerStyle: PropTypes.object,
  localization: PropTypes.object,
  selectedCount: PropTypes.number,
  sorting: PropTypes.bool,
  onAllSelected: PropTypes.func,
  onOrderChange: PropTypes.func,
  orderBy: PropTypes.number,
  orderDirection: PropTypes.string,
  actionsHeaderIndex: PropTypes.number,
  showActionsColumn: PropTypes.bool,
};

export default TableHeader;

================================================
FILE: test/fixture/react-class-js/input/react-class.js
================================================
import * as React from "react";
import PropTypes from "prop-types";
import {
  TableHead, TableRow, TableCell,
  TableSortLabel, Checkbox
} from "@material-ui/core";
import { Draggable } from "react-beautiful-dnd";

class TableHeader extends React.Component {
    
  renderHeader() {
    const mapArr = this.props.columns.filter(columnDef => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1))
      .map((columnDef, index) => (
        <TableCell
          key={columnDef.tableData.id}
          align={["numeric"].indexOf(columnDef.type) !== -1 ? "right" : "left"}

          style={{ ...this.props.headerStyle, ...columnDef.headerStyle }}
        >
          <div className="displayFlex">
            <div>
              {(columnDef.sort !== false && columnDef.sorting !== false && this.props.sorting)
                ? <TableSortLabel
                  active={this.props.orderBy === columnDef.tableData.id}
                  direction={this.props.orderDirection || "asc"}
                  onClick={() => {
                    const orderDirection = columnDef.tableData.id !== this.props.orderBy ? "asc" : this.props.orderDirection === "asc" ? "desc" : "asc";
                    this.props.onOrderChange(columnDef.tableData.id, orderDirection);
                  }}
                >
                  {(this.props.grouping && columnDef.field)
                    ? <Draggable
                      key={columnDef.tableData.id}
                      draggableId={columnDef.tableData.id.toString()}
                      index={index}>
                      {(provided) => (
                        <div
                          ref={provided.innerRef}
                          {...provided.draggableProps}
                          {...provided.dragHandleProps}                          
                        >
                          {columnDef.title}
                        </div>
                      )}
                    </Draggable>
                    : columnDef.title
                  }
                </TableSortLabel>
                : columnDef.title
              }
            </div>
            {columnDef.filter && 
                    <div>
                      {columnDef.filter}
                    </div>

            }
          </div>
        </TableCell>
      ));
    return mapArr;
  }

  renderActionsHeader() {
    const localization = { ...TableHeader.defaultProps.localization, ...this.props.localization };
    return (
      <TableCell
        key="key-actions-column"
        style={this.props.headerStyle}
      >
        <TableSortLabel>{localization.actions}</TableSortLabel>
      </TableCell>
    );
  }
  renderSelectionHeader() {
    return (
      <TableCell
        padding="none"
        key="key-selection-column"
        style={this.props.headerStyle}
      >
        <Checkbox
          indeterminate={this.props.selectedCount > 0 && this.props.selectedCount < this.props.dataCount}
          checked={this.props.selectedCount === this.props.dataCount}
          onChange={(event, checked) => this.props.onAllSelected && this.props.onAllSelected(checked)}
        />
      </TableCell>
    );
  }
  render() {
    const headers = this.renderHeader();
    if (this.props.hasSelection && this.props.dataCount) {
      headers.splice(0, 0, this.renderSelectionHeader());
    }

    if (this.props.showActionsColumn) {
      if (this.props.actionsHeaderIndex >= 0) {
        let endPos = 0;
        if (this.props.hasSelection) {
          endPos = 1;
        }
        headers.splice(this.props.actionsHeaderIndex + endPos, 0, this.renderActionsHeader());
      } else if (this.props.actionsHeaderIndex === -1) {
        headers.push(this.renderActionsHeader());
      }
    }

    if (this.props.hasDetailPanel) {
      headers.splice(0, 0,
        <TableCell
          padding="none"
          key="key-detail-panel-column"
          style={this.props.headerStyle}
        />
      );
    }

    this.props.columns
      .filter(columnDef => columnDef.tableData.groupOrder > -1)
      .forEach(columnDef => {
        headers.splice(0, 0, <TableCell padding="checkbox" key={"key-group-header" + columnDef.tableData.id} />);
      });

    return (
      <TableHead>
        <TableRow>
          {headers}
        </TableRow>
      </TableHead>
    );
  }
}

TableHeader.defaultProps = {
  dataCount: 0,
  hasSelection: false,
  headerStyle: {},
  selectedCount: 0,
  sorting: true,
  localization: {
    actions: "Actions" 
  },
  orderBy: undefined,
  orderDirection: "asc",
  actionsHeaderIndex: 0
};

TableHeader.propTypes = {
  columns: PropTypes.array.isRequired,
  dataCount: PropTypes.number,
  hasDetailPanel: PropTypes.bool.isRequired,
  hasSelection: PropTypes.bool,
  headerStyle: PropTypes.object,
  localization: PropTypes.object,
  selectedCount: PropTypes.number,
  sorting: PropTypes.bool,
  onAllSelected: PropTypes.func,
  onOrderChange: PropTypes.func,
  orderBy: PropTypes.number,
  orderDirection: PropTypes.string,
  actionsHeaderIndex: PropTypes.number,
  showActionsColumn: PropTypes.bool,
};

export default TableHeader;

================================================
FILE: test/fixture/react-class-jsx/expected/react-class.tsx
================================================
import * as React from "react";
import PropTypes from "prop-types";
import {
  TableHead, TableRow, TableCell,
  TableSortLabel, Checkbox
} from "@material-ui/core";
import { Draggable } from "react-beautiful-dnd";

class TableHeader extends React.Component {
	public props: any;

  renderHeader() {
    const mapArr = this.props.columns.filter(columnDef => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1))
      .map((columnDef, index) => (
        <TableCell
          key={columnDef.tableData.id}
          align={["numeric"].indexOf(columnDef.type) !== -1 ? "right" : "left"}

          style={{ ...this.props.headerStyle, ...columnDef.headerStyle }}
        >
          <div className="displayFlex">
            <div>
              {(columnDef.sort !== false && columnDef.sorting !== false && this.props.sorting)
                ? <TableSortLabel
                  active={this.props.orderBy === columnDef.tableData.id}
                  direction={this.props.orderDirection || "asc"}
                  onClick={() => {
                    const orderDirection = columnDef.tableData.id !== this.props.orderBy ? "asc" : this.props.orderDirection === "asc" ? "desc" : "asc";
                    this.props.onOrderChange(columnDef.tableData.id, orderDirection);
                  }}
                >
                  {(this.props.grouping && columnDef.field)
                    ? <Draggable
                      key={columnDef.tableData.id}
                      draggableId={columnDef.tableData.id.toString()}
                      index={index}>
                      {(provided) => (
                        <div
                          ref={provided.innerRef}
                          {...provided.draggableProps}
                          {...provided.dragHandleProps}                          
                        >
                          {columnDef.title}
                        </div>
                      )}
                    </Draggable>
                    : columnDef.title
                  }
                </TableSortLabel>
                : columnDef.title
              }
            </div>
            {columnDef.filter && 
                    <div>
                      {columnDef.filter}
                    </div>

            }
          </div>
        </TableCell>
      ));
    return mapArr;
  }

  renderActionsHeader() {
    const localization = { ...TableHeader.defaultProps.localization, ...this.props.localization };
    return (
      <TableCell
        key="key-actions-column"
        style={this.props.headerStyle}
      >
        <TableSortLabel>{localization.actions}</TableSortLabel>
      </TableCell>
    );
  }
  renderSelectionHeader() {
    return (
      <TableCell
        padding="none"
        key="key-selection-column"
        style={this.props.headerStyle}
      >
        <Checkbox
          indeterminate={this.props.selectedCount > 0 && this.props.selectedCount < this.props.dataCount}
          checked={this.props.selectedCount === this.props.dataCount}
          onChange={(event, checked) => this.props.onAllSelected && this.props.onAllSelected(checked)}
        />
      </TableCell>
    );
  }
  render() {
    const headers = this.renderHeader();
    if (this.props.hasSelection && this.props.dataCount) {
      headers.splice(0, 0, this.renderSelectionHeader());
    }

    if (this.props.showActionsColumn) {
      if (this.props.actionsHeaderIndex >= 0) {
        let endPos = 0;
        if (this.props.hasSelection) {
          endPos = 1;
        }
        headers.splice(this.props.actionsHeaderIndex + endPos, 0, this.renderActionsHeader());
      } else if (this.props.actionsHeaderIndex === -1) {
        headers.push(this.renderActionsHeader());
      }
    }

    if (this.props.hasDetailPanel) {
      headers.splice(0, 0,
        <TableCell
          padding="none"
          key="key-detail-panel-column"
          style={this.props.headerStyle}
        />
      );
    }

    this.props.columns
      .filter(columnDef => columnDef.tableData.groupOrder > -1)
      .forEach(columnDef => {
        headers.splice(0, 0, <TableCell padding="checkbox" key={"key-group-header" + columnDef.tableData.id} />);
      });

    return (
      <TableHead>
        <TableRow>
          {headers}
        </TableRow>
      </TableHead>
    );
  }
}

TableHeader.defaultProps = {
  dataCount: 0,
  hasSelection: false,
  headerStyle: {},
  selectedCount: 0,
  sorting: true,
  localization: {
    actions: "Actions" 
  },
  orderBy: undefined,
  orderDirection: "asc",
  actionsHeaderIndex: 0
};

TableHeader.propTypes = {
  columns: PropTypes.array.isRequired,
  dataCount: PropTypes.number,
  hasDetailPanel: PropTypes.bool.isRequired,
  hasSelection: PropTypes.bool,
  headerStyle: PropTypes.object,
  localization: PropTypes.object,
  selectedCount: PropTypes.number,
  sorting: PropTypes.bool,
  onAllSelected: PropTypes.func,
  onOrderChange: PropTypes.func,
  orderBy: PropTypes.number,
  orderDirection: PropTypes.string,
  actionsHeaderIndex: PropTypes.number,
  showActionsColumn: PropTypes.bool,
};

export default TableHeader;

================================================
FILE: test/fixture/react-class-jsx/input/react-class.jsx
================================================
import * as React from "react";
import PropTypes from "prop-types";
import {
  TableHead, TableRow, TableCell,
  TableSortLabel, Checkbox
} from "@material-ui/core";
import { Draggable } from "react-beautiful-dnd";

class TableHeader extends React.Component {
    
  renderHeader() {
    const mapArr = this.props.columns.filter(columnDef => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1))
      .map((columnDef, index) => (
        <TableCell
          key={columnDef.tableData.id}
          align={["numeric"].indexOf(columnDef.type) !== -1 ? "right" : "left"}

          style={{ ...this.props.headerStyle, ...columnDef.headerStyle }}
        >
          <div className="displayFlex">
            <div>
              {(columnDef.sort !== false && columnDef.sorting !== false && this.props.sorting)
                ? <TableSortLabel
                  active={this.props.orderBy === columnDef.tableData.id}
                  direction={this.props.orderDirection || "asc"}
                  onClick={() => {
                    const orderDirection = columnDef.tableData.id !== this.props.orderBy ? "asc" : this.props.orderDirection === "asc" ? "desc" : "asc";
                    this.props.onOrderChange(columnDef.tableData.id, orderDirection);
                  }}
                >
                  {(this.props.grouping && columnDef.field)
                    ? <Draggable
                      key={columnDef.tableData.id}
                      draggableId={columnDef.tableData.id.toString()}
                      index={index}>
                      {(provided) => (
                        <div
                          ref={provided.innerRef}
                          {...provided.draggableProps}
                          {...provided.dragHandleProps}                          
                        >
                          {columnDef.title}
                        </div>
                      )}
                    </Draggable>
                    : columnDef.title
                  }
                </TableSortLabel>
                : columnDef.title
              }
            </div>
            {columnDef.filter && 
                    <div>
                      {columnDef.filter}
                    </div>

            }
          </div>
        </TableCell>
      ));
    return mapArr;
  }

  renderActionsHeader() {
    const localization = { ...TableHeader.defaultProps.localization, ...this.props.localization };
    return (
      <TableCell
        key="key-actions-column"
        style={this.props.headerStyle}
      >
        <TableSortLabel>{localization.actions}</TableSortLabel>
      </TableCell>
    );
  }
  renderSelectionHeader() {
    return (
      <TableCell
        padding="none"
        key="key-selection-column"
        style={this.props.headerStyle}
      >
        <Checkbox
          indeterminate={this.props.selectedCount > 0 && this.props.selectedCount < this.props.dataCount}
          checked={this.props.selectedCount === this.props.dataCount}
          onChange={(event, checked) => this.props.onAllSelected && this.props.onAllSelected(checked)}
        />
      </TableCell>
    );
  }
  render() {
    const headers = this.renderHeader();
    if (this.props.hasSelection && this.props.dataCount) {
      headers.splice(0, 0, this.renderSelectionHeader());
    }

    if (this.props.showActionsColumn) {
      if (this.props.actionsHeaderIndex >= 0) {
        let endPos = 0;
        if (this.props.hasSelection) {
          endPos = 1;
        }
        headers.splice(this.props.actionsHeaderIndex + endPos, 0, this.renderActionsHeader());
      } else if (this.props.actionsHeaderIndex === -1) {
        headers.push(this.renderActionsHeader());
      }
    }

    if (this.props.hasDetailPanel) {
      headers.splice(0, 0,
        <TableCell
          padding="none"
          key="key-detail-panel-column"
          style={this.props.headerStyle}
        />
      );
    }

    this.props.columns
      .filter(columnDef => columnDef.tableData.groupOrder > -1)
      .forEach(columnDef => {
        headers.splice(0, 0, <TableCell padding="checkbox" key={"key-group-header" + columnDef.tableData.id} />);
      });

    return (
      <TableHead>
        <TableRow>
          {headers}
        </TableRow>
      </TableHead>
    );
  }
}

TableHeader.defaultProps = {
  dataCount: 0,
  hasSelection: false,
  headerStyle: {},
  selectedCount: 0,
  sorting: true,
  localization: {
    actions: "Actions" 
  },
  orderBy: undefined,
  orderDirection: "asc",
  actionsHeaderIndex: 0
};

TableHeader.propTypes = {
  columns: PropTypes.array.isRequired,
  dataCount: PropTypes.number,
  hasDetailPanel: PropTypes.bool.isRequired,
  hasSelection: PropTypes.bool,
  headerStyle: PropTypes.object,
  localization: PropTypes.object,
  selectedCount: PropTypes.number,
  sorting: PropTypes.bool,
  onAllSelected: PropTypes.func,
  onOrderChange: PropTypes.func,
  orderBy: PropTypes.number,
  orderDirection: PropTypes.string,
  actionsHeaderIndex: PropTypes.number,
  showActionsColumn: PropTypes.bool,
};

export default TableHeader;

================================================
FILE: test/fixture/react-jsx-self-closing-element/expected/react-self-closing-element.tsx
================================================
import * as React from "react";

/**
 * This example makes sure that .js is converted to .tsx when the only JSX
 * element in the file is self-closing
 */
export const MyComponent = () => {
  return <div/>;
};

================================================
FILE: test/fixture/react-jsx-self-closing-element/input/react-self-closing-element.js
================================================
import * as React from "react";

/**
 * This example makes sure that .js is converted to .tsx when the only JSX
 * element in the file is self-closing
 */
export const MyComponent = () => {
  return <div/>;
};

================================================
FILE: test/fixture/superclass-subclass/expected/another-sub-class.ts
================================================
import DefaultExportClass from "./default-export-class";

export class AnotherSubClass extends DefaultExportClass {
	public anotherSubClassProp: any;

	constructor() {
		this.defaultExportClassProp = 45;  // from superclass
		this.anotherSubClassProp = 10;
	}
}

================================================
FILE: test/fixture/superclass-subclass/expected/default-export-class.ts
================================================
class DefaultExportClass {
	public defaultExportClassProp: any;

	constructor() {
		this.defaultExportClassProp = 1;
	}

}

export default DefaultExportClass;

================================================
FILE: test/fixture/superclass-subclass/expected/my-class.ts
================================================
import { MySuperClass } from './my-super-class';

export class MyClass extends MySuperClass {
	public myClassProp1: any;
	public myClassProp2: any;
	public myClassProp3: any;

	constructor() {
		this.mySuperClassProp = 99;
		this.myClassProp1 = 42;
		this.doSomething();  // should not become a property
		this.mySuperclassMethod();  // should not become a property as it is a method in the superclass
	}

	doSomething() {
		this.myClassProp2 = 78;
		console.log( this.myClassProp3 );
	}

}

================================================
FILE: test/fixture/superclass-subclass/expected/my-sub-class.ts
================================================
import { MyClass } from "./my-class";

export class MySubClass extends MyClass {
	public mySubClassProp: any;

	constructor() {
		this.mySuperClassProp = 42;  // from superclass's superclass - should not be added as a prop
		this.myClassProp1 = 43;  // from superclass - should not be added as a prop
		this.mySubClassProp = 1;
		this.mySuperclassMethod();  // should not be added as a property as it exists two superclasses up
	}
}

================================================
FILE: test/fixture/superclass-subclass/expected/my-super-class.ts
================================================
export class MySuperClass {
	public mySuperClassProp: any;

	constructor() {
		this.mySuperclassMethod();  // should not be added as a property
	}

	mySuperclassMethod() {
		this.mySuperClassProp = 10;
	}

}

================================================
FILE: test/fixture/superclass-subclass/expected/superclass-in-node-modules.ts
================================================
import { Subject } from 'rxjs';

export class MySubClassWithSuperClassInNodeModules extends Subject {
	public myProp: any;

	mySuperclassMethod() {
		this.myProp = 10;
	}

}

================================================
FILE: test/fixture/superclass-subclass/expected/two-classes.ts
================================================
class Super {
	public superProp: any;

	someMethod() {
		this.superProp = 1;
	}
}


class Sub extends Super {
	public subProp: any;

	someMethod() {
		this.superProp = 2;
		this.subProp = 2;
	}
}

================================================
FILE: test/fixture/superclass-subclass/input/another-sub-class.js
================================================
import DefaultExportClass from "./default-export-class";

export class AnotherSubClass extends DefaultExportClass {
	constructor() {
		this.defaultExportClassProp = 45;  // from superclass
		this.anotherSubClassProp = 10;
	}
}

================================================
FILE: test/fixture/superclass-subclass/input/default-export-class.js
================================================
class DefaultExportClass {

	constructor() {
		this.defaultExportClassProp = 1;
	}

}

export default DefaultExportClass;

================================================
FILE: test/fixture/superclass-subclass/input/my-class.js
================================================
import { MySuperClass } from './my-super-class';

export class MyClass extends MySuperClass {

	constructor() {
		this.mySuperClassProp = 99;
		this.myClassProp1 = 42;
		this.doSomething();  // should not become a property
		this.mySuperclassMethod();  // should not become a property as it is a method in the superclass
	}

	doSomething() {
		this.myClassProp2 = 78;
		console.log( this.myClassProp3 );
	}

}

================================================
FILE: test/fixture/superclass-subclass/input/my-sub-class.js
================================================
import { MyClass } from "./my-class";

export class MySubClass extends MyClass {
	constructor() {
		this.mySuperClassProp = 42;  // from superclass's superclass - should not be added as a prop
		this.myClassProp1 = 43;  // from superclass - should not be added as a prop
		this.mySubClassProp = 1;
		this.mySuperclassMethod();  // should not be added as a property as it exists two superclasses up
	}
}

================================================
FILE: test/fixture/superclass-subclass/input/my-super-class.js
================================================
export class MySuperClass {

	constructor() {
		this.mySuperclassMethod();  // should not be added as a property
	}

	mySuperclassMethod() {
		this.mySuperClassProp = 10;
	}

}

================================================
FILE: test/fixture/superclass-subclass/input/package.json
================================================
{
  "devDependencies": {},
  "dependencies": {
    "rxjs": "^6.2.2"
  }
}


================================================
FILE: test/fixture/superclass-subclass/input/superclass-in-node-modules.js
================================================
import { Subject } from 'rxjs';

export class MySubClassWithSuperClassInNodeModules extends Subject {

	mySuperclassMethod() {
		this.myProp = 10;
	}

}

================================================
FILE: test/fixture/superclass-subclass/input/two-classes.js
================================================
class Super {
	someMethod() {
		this.superProp = 1;
	}
}


class Sub extends Super {
	someMethod() {
		this.superProp = 2;
		this.subProp = 2;
	}
}

================================================
FILE: test/fixture/superclass-subclass-node-modules-not-installed/expected/superclass-in-node-modules.ts
================================================
import { SomeSuperclass } from 'some-not-installed-module';

export class MyClassWithSuperClassInNodeModules extends SomeSuperclass {
	public myProp: any;

	myMethod() {
		this.myProp = 10;
	}

}

================================================
FILE: test/fixture/superclass-subclass-node-modules-not-installed/input/superclass-in-node-modules.js
================================================
import { SomeSuperclass } from 'some-not-installed-module';

export class MyClassWithSuperClassInNodeModules extends SomeSuperclass {

	myMethod() {
		this.myProp = 10;
	}

}

================================================
FILE: test/fixture/typescript-class/expected/declarations-in-superclass.ts
================================================
class SuperTypeScriptClass {
	public superProp: any;  // *declaration* that should not be added to subclass
}

class SubTypeScriptClass extends SuperTypeScriptClass {
	public subProp: any;

	constructor() {
		super();
		this.superProp = 1;  // should not be added as a declaration in this class
		this.subProp = 2;    // *should* be filled in as it is currently missing in this class and its superclass
	}
}

class SubSubTypeScriptClass extends SubTypeScriptClass {
	constructor() {
		super();
		this.superProp = 1;  // should not be added as a declaration in this class as it is declared 2 superclasses up
	}
}

================================================
FILE: test/fixture/typescript-class/expected/typescript-class.ts
================================================
export class TypescriptClass {
	public prop: any;  // shouldn't be duplicated

	constructor() {
		this.prop = 1;
	}
}

================================================
FILE: test/fixture/typescript-class/expected/typescript-sub-class.ts
================================================
import { TypescriptClass } from "./typescript-class";

export class TypescriptSubClass extends TypescriptClass {
	public prop2: any;

	constructor() {
		super();
		this.prop2 = 1;
	}
}

================================================
FILE: test/fixture/typescript-class/input/declarations-in-superclass.ts
================================================
class SuperTypeScriptClass {
	public superProp: any;  // *declaration* that should not be added to subclass
}

class SubTypeScriptClass extends SuperTypeScriptClass {
	constructor() {
		super();
		this.superProp = 1;  // should not be added as a declaration in this class
		this.subProp = 2;    // *should* be filled in as it is currently missing in this class and its superclass
	}
}

class SubSubTypeScriptClass extends SubTypeScriptClass {
	constructor() {
		super();
		this.superProp = 1;  // should not be added as a declaration in this class as it is declared 2 superclasses up
	}
}

================================================
FILE: test/fixture/typescript-class/input/typescript-class.ts
================================================
export class TypescriptClass {
	public prop: any;  // shouldn't be duplicated

	constructor() {
		this.prop = 1;
	}
}

================================================
FILE: test/fixture/typescript-class/input/typescript-sub-class.ts
================================================
import { TypescriptClass } from "./typescript-class";

export class TypescriptSubClass extends TypescriptClass {
	constructor() {
		super();
		this.prop2 = 1;
	}
}

================================================
FILE: test.ts
================================================
import { Project } from "ts-morph";

const tsAstProject = new Project();
const sourceFile = tsAstProject.createSourceFile('testfile.js', getSourceText());

// Just calling the below method is what causes the problem when moving later. 
// If this line is commented out, the move succeeds.
sourceFile.getClass( 'TableHeader' )!;

sourceFile.move('testfile.tsx');


function getSourceText() {
    return `
        class TableHeader extends React.Component {
            renderHeader() {
                const mapArr = this.props.columns
                    .map(columnDef => (
                        <div>
                            {(columnDef.sort !== false)
                                ? 'test'
                                : 'title'
                            }
                        </div>
                    ));

                return mapArr;
            }
        }
    `;
}


================================================
FILE: tsconfig.json
================================================
{
	"compilerOptions": {
		/* Basic Options */
		"target": "es2017",                       /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
		"module": "commonjs",                     /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
		"skipLibCheck": true,
		// "lib": [],                             /* Specify library files to be included in the compilation:  */
		// "allowJs": true,                       /* Allow javascript files to be compiled. */
		// "checkJs": true,                       /* Report errors in .js files. */
		// "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
		"declaration": true,                      /* Generates corresponding '.d.ts' file. */
		// "sourceMap": true,                     /* Generates corresponding '.map' file. */
		// "outFile": "./",                       /* Concatenate and emit output to single file. */
		"outDir": "./dist",                       /* Redirect output structure to the directory. */
		// "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
		// "removeComments": true,                /* Do not emit comments to output. */
		// "noEmit": true,                        /* Do not emit outputs. */
		// "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
		// "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
		// "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
		"esModuleInterop": true,

		/* Strict Type-Checking Options */
		"strict": true,                           /* Enable all strict type-checking options. */
		// "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
		// "strictNullChecks": true,              /* Enable strict null checks. */
		// "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
		// "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

		/* Additional Checks */
		// "noUnusedLocals": true,                /* Report errors on unused locals. */
		// "noUnusedParameters": true,            /* Report errors on unused parameters. */
		"noImplicitReturns": true,                /* Report error when not all code paths in function return a value. */
		"noFallthroughCasesInSwitch": true,       /* Report errors for fallthrough cases in switch statement. */

		/* Module Resolution Options */
		// "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
		"baseUrl": "./src",                       /* Base directory to resolve non-absolute module names. */
		// "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
		// "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
		// "typeRoots": [],                       /* List of folders to include type definitions from. */
		// "types": [],                           /* Type declaration files to be included in compilation. */
		// "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */

		/* Source Map Options */
		// "sourceRoot": "./",                    /* Specify the location where debugger should locate TypeScript files instead of source locations. */
		// "mapRoot": "./",                       /* Specify the location where debugger should locate map files instead of generated locations. */
		"inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
		"inlineSources": true                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

		/* Experimental Options */
		// "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
		// "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
	},
	"include": [
		"src/**/*.ts"
	]
}
Download .txt
gitextract_7dpmj4fc/

├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── src/
│   ├── cli.ts
│   ├── converter/
│   │   ├── add-class-property-declarations/
│   │   │   ├── add-class-property-declarations.ts
│   │   │   ├── correct-js-properties.ts
│   │   │   ├── js-class.ts
│   │   │   ├── parse-js-classes.ts
│   │   │   └── parse-superclass-name-and-path.ts
│   │   ├── add-optionals-to-function-params.ts
│   │   ├── convert.ts
│   │   └── filter-out-node-modules.ts
│   ├── create-ts-morph-project.ts
│   ├── index.ts
│   ├── js-to-ts-converter.ts
│   ├── logger/
│   │   ├── index.ts
│   │   ├── log-level.ts
│   │   └── logger.ts
│   └── util/
│       ├── find-import-for-identifier.ts
│       ├── is-element-access-with-obj.ts
│       ├── is-property-access-with-obj.ts
│       ├── is-property-or-elemement-access-with-obj.ts
│       ├── is-this-referencing-var.ts
│       ├── is-valid-identifier.ts
│       ├── parse-destructured-props.ts
│       └── set-utils.ts
├── test/
│   ├── convert.spec.ts
│   └── fixture/
│       ├── class-with-this-constructor-reference/
│       │   ├── expected/
│       │   │   └── my-class.ts
│       │   └── input/
│       │       └── my-class.js
│       ├── expression-extends/
│       │   ├── expected/
│       │   │   └── expression-extends.ts
│       │   └── input/
│       │       └── expression-extends.js
│       ├── function-calls-with-fewer-args-than-params/
│       │   ├── expected/
│       │   │   ├── call-to-exported-function.ts
│       │   │   ├── call-to-local-function-with-default-value.ts
│       │   │   ├── call-to-local-function.ts
│       │   │   ├── call-to-sub-class-method.ts
│       │   │   ├── call-to-super-class-method.ts
│       │   │   ├── constructor-with-rest-param.ts
│       │   │   ├── exported-function.ts
│       │   │   ├── sub-class.ts
│       │   │   └── super-class.ts
│       │   └── input/
│       │       ├── call-to-exported-function.js
│       │       ├── call-to-local-function-with-default-value.js
│       │       ├── call-to-local-function.js
│       │       ├── call-to-sub-class-method.js
│       │       ├── call-to-super-class-method.js
│       │       ├── constructor-with-rest-param.js
│       │       ├── exported-function.js
│       │       ├── sub-class.js
│       │       └── super-class.js
│       ├── function-expressions-and-declarations/
│       │   ├── expected/
│       │   │   └── class-with-function-expressions.ts
│       │   └── input/
│       │       └── class-with-function-expressions.js
│       ├── include-exclude-patterns/
│       │   ├── expected/
│       │   │   └── included/
│       │   │       └── included-file.ts
│       │   └── input/
│       │       ├── included/
│       │       │   ├── excluded/
│       │       │   │   └── excluded-file.js
│       │       │   └── included-file.js
│       │       └── other-file-that-should-not-be-included.js
│       ├── react-class-js/
│       │   ├── expected/
│       │   │   └── react-class.tsx
│       │   └── input/
│       │       └── react-class.js
│       ├── react-class-jsx/
│       │   ├── expected/
│       │   │   └── react-class.tsx
│       │   └── input/
│       │       └── react-class.jsx
│       ├── react-jsx-self-closing-element/
│       │   ├── expected/
│       │   │   └── react-self-closing-element.tsx
│       │   └── input/
│       │       └── react-self-closing-element.js
│       ├── superclass-subclass/
│       │   ├── expected/
│       │   │   ├── another-sub-class.ts
│       │   │   ├── default-export-class.ts
│       │   │   ├── my-class.ts
│       │   │   ├── my-sub-class.ts
│       │   │   ├── my-super-class.ts
│       │   │   ├── superclass-in-node-modules.ts
│       │   │   └── two-classes.ts
│       │   └── input/
│       │       ├── another-sub-class.js
│       │       ├── default-export-class.js
│       │       ├── my-class.js
│       │       ├── my-sub-class.js
│       │       ├── my-super-class.js
│       │       ├── package.json
│       │       ├── superclass-in-node-modules.js
│       │       └── two-classes.js
│       ├── superclass-subclass-node-modules-not-installed/
│       │   ├── expected/
│       │   │   └── superclass-in-node-modules.ts
│       │   └── input/
│       │       └── superclass-in-node-modules.js
│       └── typescript-class/
│           ├── expected/
│           │   ├── declarations-in-superclass.ts
│           │   ├── typescript-class.ts
│           │   └── typescript-sub-class.ts
│           └── input/
│               ├── declarations-in-superclass.ts
│               ├── typescript-class.ts
│               └── typescript-sub-class.ts
├── test.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (192 symbols across 71 files)

FILE: src/cli.ts
  function resolveIndentationText (line 75) | function resolveIndentationText( indentationText: 'tab' | 'twospaces' | ...
  function resolveLogLevel (line 87) | function resolveLogLevel( logLevelStr: string ): LogLevel {
  function parseIncludePatterns (line 99) | function parseIncludePatterns( includePatterns: string | undefined ): st...
  function parseExcludePatterns (line 105) | function parseExcludePatterns( excludePatterns: string | undefined ): st...

FILE: src/converter/add-class-property-declarations/add-class-property-declarations.ts
  function addClassPropertyDeclarations (line 28) | function addClassPropertyDeclarations( tsAstProject: Project ): Project {

FILE: src/converter/add-class-property-declarations/correct-js-properties.ts
  function correctJsProperties (line 25) | function correctJsProperties( jsClasses: JsClass[] ): JsClass[] {

FILE: src/converter/add-class-property-declarations/js-class.ts
  class JsClass (line 6) | class JsClass {
    method constructor (line 57) | constructor( cfg: {
    method id (line 79) | public get id(): string {
    method superclassId (line 90) | public get superclassId(): string | undefined {
    method isSuperclassInNodeModules (line 110) | public isSuperclassInNodeModules(): boolean {

FILE: src/converter/add-class-property-declarations/parse-js-classes.ts
  function parseJsClasses (line 42) | function parseJsClasses( tsAstProject: Project ): JsClass[] {
  function parseFileClasses (line 60) | function parseFileClasses( sourceFile: SourceFile ): JsClass[] {
  function getMethodNames (line 86) | function getMethodNames( fileClass: ClassDeclaration ): Set<string> {
  function getPropertyNames (line 98) | function getPropertyNames( fileClass: ClassDeclaration ) {
  function parsePropertyDeclarations (line 118) | function parsePropertyDeclarations( fileClass: ClassDeclaration ): Set<s...
  function parsePropertyAccesses (line 141) | function parsePropertyAccesses( fileClass: ClassDeclaration ): Set<strin...
  function parseDestructuringThisAssignments (line 163) | function parseDestructuringThisAssignments( fileClass: ClassDeclaration ...
  function parsePropertyAccessesOfThisAssignedVars (line 194) | function parsePropertyAccessesOfThisAssignedVars(

FILE: src/converter/add-class-property-declarations/parse-superclass-name-and-path.ts
  function parseSuperclassNameAndPath (line 14) | function parseSuperclassNameAndPath(
  function findImportPathForIdentifier (line 66) | function findImportPathForIdentifier(

FILE: src/converter/add-optionals-to-function-params.ts
  type NameableFunction (line 4) | type NameableFunction = FunctionDeclaration | MethodDeclaration;
  type FunctionTransformTarget (line 5) | type FunctionTransformTarget = NameableFunction | ConstructorDeclaration;
  function addOptionalsToFunctionParams (line 30) | function addOptionalsToFunctionParams( tsAstProject: Project ): Project {
  function parseClassConstructorCalls (line 55) | function parseClassConstructorCalls( sourceFiles: SourceFile[] ): Map<Co...
  function parseFunctionAndMethodCalls (line 111) | function parseFunctionAndMethodCalls( sourceFiles: SourceFile[] ): Map<N...
  function getFunctionsAndMethods (line 153) | function getFunctionsAndMethods(
  function addOptionals (line 187) | function addOptionals( minArgsMap: Map<FunctionTransformTarget, number> ) {

FILE: src/converter/convert.ts
  function convert (line 10) | function convert( tsAstProject: Project ): Project {
  function printSourceFilesList (line 81) | function printSourceFilesList( astProject: Project, indent = '' ) {

FILE: src/converter/filter-out-node-modules.ts
  function filterOutNodeModules (line 9) | function filterOutNodeModules( tsAstProject: Project ): Project {

FILE: src/create-ts-morph-project.ts
  function createTsMorphProject (line 15) | function createTsMorphProject( directory: string, options: {

FILE: src/js-to-ts-converter.ts
  type JsToTsConverterOptions (line 8) | interface JsToTsConverterOptions {
  function convertJsToTs (line 28) | async function convertJsToTs(
  function convertJsToTsSync (line 51) | function convertJsToTsSync(
  function doConvert (line 75) | function doConvert(

FILE: src/logger/log-level.ts
  type LogLevel (line 1) | type LogLevel = 'debug' | 'verbose' | 'info' | 'warn' | 'error';

FILE: src/logger/logger.ts
  class Logger (line 22) | class Logger {
    method setLogLevel (line 24) | setLogLevel( logLevel: LogLevel ) {
    method debug (line 28) | debug( message: string ) {
    method verbose (line 32) | verbose( message: string ) {
    method info (line 36) | info( message: string ) {
    method log (line 40) | log( message: string ) {
    method warn (line 44) | warn( message: string ) {
    method error (line 48) | error( message: string ) {

FILE: src/util/find-import-for-identifier.ts
  function findImportForIdentifier (line 19) | function findImportForIdentifier(

FILE: src/util/is-element-access-with-obj.ts
  function isElementAccessWithObj (line 15) | function isElementAccessWithObj(
  function elementAccessWithObjFilter (line 58) | function elementAccessWithObjFilter( objIdentifier: string ): ( node: No...

FILE: src/util/is-property-access-with-obj.ts
  function isPropertyAccessWithObj (line 15) | function isPropertyAccessWithObj(
  function propertyAccessWithObjFilter (line 58) | function propertyAccessWithObjFilter( objIdentifier: string ): ( node: N...

FILE: src/util/is-property-or-elemement-access-with-obj.ts
  function isPropertyOrElemementAccessWithObj (line 17) | function isPropertyOrElemementAccessWithObj(
  function propertyOrElementAccessWithObjFilter (line 51) | function propertyOrElementAccessWithObjFilter( objIdentifier: string ): ...

FILE: src/util/is-this-referencing-var.ts
  function isThisReferencingVar (line 14) | function isThisReferencingVar( node: Node ): node is VariableDeclaration {

FILE: src/util/is-valid-identifier.ts
  function isValidIdentifier (line 4) | function isValidIdentifier( text: string ) {

FILE: src/util/parse-destructured-props.ts
  function parseDestructuredProps (line 17) | function parseDestructuredProps( node: ts.ObjectBindingPattern ): string...

FILE: src/util/set-utils.ts
  function union (line 5) | function union<T>( setA: Set<T>, ...sets: Set<T>[] ) {
  function difference (line 21) | function difference<T>( setA: Set<T>, setB: Set<T> ) {

FILE: test.ts
  function getSourceText (line 13) | function getSourceText() {

FILE: test/convert.spec.ts
  function runTest (line 105) | function runTest(

FILE: test/fixture/class-with-this-constructor-reference/expected/my-class.ts
  class MyClass (line 1) | class MyClass {
    method constructor (line 4) | constructor() {}
    method myMethod (line 6) | myMethod() {

FILE: test/fixture/class-with-this-constructor-reference/input/my-class.js
  class MyClass (line 1) | class MyClass {
    method constructor (line 3) | constructor() {}
    method myMethod (line 5) | myMethod() {

FILE: test/fixture/expression-extends/expected/expression-extends.ts
  class ExpressionExtends (line 1) | class ExpressionExtends extends Mixin.mix( SomeClass1, SomeClass2 ) {
    method constructor (line 4) | constructor() {

FILE: test/fixture/expression-extends/input/expression-extends.js
  class ExpressionExtends (line 1) | class ExpressionExtends extends Mixin.mix( SomeClass1, SomeClass2 ) {
    method constructor (line 2) | constructor() {

FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-local-function-with-default-value.ts
  function threeArg (line 1) | function threeArg( arg1, arg2 = 1, arg3 = 2 ) {

FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-local-function.ts
  function threeArg (line 1) | function threeArg( arg1, arg2?, arg3? ) {

FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/constructor-with-rest-param.ts
  class ConstructorWithRestParam (line 1) | class ConstructorWithRestParam {
    method constructor (line 3) | constructor( ...args ) {  // should *not* be marked as optional
    method methodWithRestParam (line 7) | methodWithRestParam( ...args ) {}  // should *not* be marked as optional

FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/exported-function.ts
  function myExportedFunction (line 1) | function myExportedFunction( arg1?, arg2? ) {  // arg1 and arg2 made opt...

FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/sub-class.ts
  class SubClass (line 3) | class SubClass extends SuperClass {
    method subclassMethod (line 5) | subclassMethod( arg1, arg2 ) {  // these should *not* be made optional...
    method subclassMethod2 (line 11) | subclassMethod2( arg1?, arg2? ) {  // these should both be made option...

FILE: test/fixture/function-calls-with-fewer-args-than-params/expected/super-class.ts
  class SuperClass (line 1) | class SuperClass {
    method constructor (line 3) | constructor( arg1, arg2?, arg3? ) {  // arg2 and arg3 will be marked o...
    method superclassMethod (line 8) | superclassMethod( arg? ) {  // arg will be marked optional by sub-clas...
    method somePublicMethod (line 13) | somePublicMethod( arg1, arg2? ) {  // arg2 will be marked optional by ...

FILE: test/fixture/function-calls-with-fewer-args-than-params/input/call-to-local-function-with-default-value.js
  function threeArg (line 1) | function threeArg( arg1, arg2 = 1, arg3 = 2 ) {

FILE: test/fixture/function-calls-with-fewer-args-than-params/input/call-to-local-function.js
  function threeArg (line 1) | function threeArg( arg1, arg2, arg3 ) {

FILE: test/fixture/function-calls-with-fewer-args-than-params/input/constructor-with-rest-param.js
  class ConstructorWithRestParam (line 1) | class ConstructorWithRestParam {
    method constructor (line 3) | constructor( ...args ) {  // should *not* be marked as optional
    method methodWithRestParam (line 7) | methodWithRestParam( ...args ) {}

FILE: test/fixture/function-calls-with-fewer-args-than-params/input/exported-function.js
  function myExportedFunction (line 1) | function myExportedFunction( arg1, arg2 ) {  // arg1 and arg2 made optio...

FILE: test/fixture/function-calls-with-fewer-args-than-params/input/sub-class.js
  class SubClass (line 3) | class SubClass extends SuperClass {
    method subclassMethod (line 5) | subclassMethod( arg1, arg2 ) {  // these should *not* be made optional...
    method subclassMethod2 (line 11) | subclassMethod2( arg1, arg2 ) {  // these should both be made optional...

FILE: test/fixture/function-calls-with-fewer-args-than-params/input/super-class.js
  class SuperClass (line 1) | class SuperClass {
    method constructor (line 3) | constructor( arg1, arg2, arg3 ) {  // arg2 and arg3 will be marked opt...
    method superclassMethod (line 8) | superclassMethod( arg ) {  // arg will be marked optional by sub-class.js
    method somePublicMethod (line 13) | somePublicMethod( arg1, arg2 ) {  // arg2 will be marked optional by c...

FILE: test/fixture/function-expressions-and-declarations/expected/class-with-function-expressions.ts
  class ClassWithFunctionExpressions (line 1) | class ClassWithFunctionExpressions {
    method myMethod (line 9) | myMethod() {
    method myMethod2 (line 22) | myMethod2() {
    method myMethod3 (line 35) | myMethod3() {
    method destructuredThis (line 44) | destructuredThis() {
    method complexMethodWhichCausesErrorInTsSimpleAstTransforms (line 50) | complexMethodWhichCausesErrorInTsSimpleAstTransforms() {

FILE: test/fixture/function-expressions-and-declarations/input/class-with-function-expressions.js
  class ClassWithFunctionExpressions (line 1) | class ClassWithFunctionExpressions {
    method myMethod (line 3) | myMethod() {
    method myMethod2 (line 16) | myMethod2() {
    method myMethod3 (line 29) | myMethod3() {
    method destructuredThis (line 38) | destructuredThis() {
    method complexMethodWhichCausesErrorInTsSimpleAstTransforms (line 44) | complexMethodWhichCausesErrorInTsSimpleAstTransforms() {

FILE: test/fixture/include-exclude-patterns/expected/included/included-file.ts
  class IncludedFile (line 1) | class IncludedFile {
    method constructor (line 4) | constructor() {

FILE: test/fixture/include-exclude-patterns/input/included/excluded/excluded-file.js
  class ExcludedFile (line 1) | class ExcludedFile {
    method constructor (line 2) | constructor() {

FILE: test/fixture/include-exclude-patterns/input/included/included-file.js
  class IncludedFile (line 1) | class IncludedFile {
    method constructor (line 2) | constructor() {

FILE: test/fixture/include-exclude-patterns/input/other-file-that-should-not-be-included.js
  class OtherFileThatShouldNotBeIncluded (line 1) | class OtherFileThatShouldNotBeIncluded {}

FILE: test/fixture/react-class-js/expected/react-class.tsx
  class TableHeader (line 9) | class TableHeader extends React.Component {
    method renderHeader (line 12) | renderHeader() {
    method renderActionsHeader (line 65) | renderActionsHeader() {
    method renderSelectionHeader (line 76) | renderSelectionHeader() {
    method render (line 91) | render() {

FILE: test/fixture/react-class-js/input/react-class.js
  class TableHeader (line 9) | class TableHeader extends React.Component {
    method renderHeader (line 11) | renderHeader() {
    method renderActionsHeader (line 64) | renderActionsHeader() {
    method renderSelectionHeader (line 75) | renderSelectionHeader() {
    method render (line 90) | render() {

FILE: test/fixture/react-class-jsx/expected/react-class.tsx
  class TableHeader (line 9) | class TableHeader extends React.Component {
    method renderHeader (line 12) | renderHeader() {
    method renderActionsHeader (line 65) | renderActionsHeader() {
    method renderSelectionHeader (line 76) | renderSelectionHeader() {
    method render (line 91) | render() {

FILE: test/fixture/react-class-jsx/input/react-class.jsx
  class TableHeader (line 9) | class TableHeader extends React.Component {
    method renderHeader (line 11) | renderHeader() {
    method renderActionsHeader (line 64) | renderActionsHeader() {
    method renderSelectionHeader (line 75) | renderSelectionHeader() {
    method render (line 90) | render() {

FILE: test/fixture/superclass-subclass-node-modules-not-installed/expected/superclass-in-node-modules.ts
  class MyClassWithSuperClassInNodeModules (line 3) | class MyClassWithSuperClassInNodeModules extends SomeSuperclass {
    method myMethod (line 6) | myMethod() {

FILE: test/fixture/superclass-subclass-node-modules-not-installed/input/superclass-in-node-modules.js
  class MyClassWithSuperClassInNodeModules (line 3) | class MyClassWithSuperClassInNodeModules extends SomeSuperclass {
    method myMethod (line 5) | myMethod() {

FILE: test/fixture/superclass-subclass/expected/another-sub-class.ts
  class AnotherSubClass (line 3) | class AnotherSubClass extends DefaultExportClass {
    method constructor (line 6) | constructor() {

FILE: test/fixture/superclass-subclass/expected/default-export-class.ts
  class DefaultExportClass (line 1) | class DefaultExportClass {
    method constructor (line 4) | constructor() {

FILE: test/fixture/superclass-subclass/expected/my-class.ts
  class MyClass (line 3) | class MyClass extends MySuperClass {
    method constructor (line 8) | constructor() {
    method doSomething (line 15) | doSomething() {

FILE: test/fixture/superclass-subclass/expected/my-sub-class.ts
  class MySubClass (line 3) | class MySubClass extends MyClass {
    method constructor (line 6) | constructor() {

FILE: test/fixture/superclass-subclass/expected/my-super-class.ts
  class MySuperClass (line 1) | class MySuperClass {
    method constructor (line 4) | constructor() {
    method mySuperclassMethod (line 8) | mySuperclassMethod() {

FILE: test/fixture/superclass-subclass/expected/superclass-in-node-modules.ts
  class MySubClassWithSuperClassInNodeModules (line 3) | class MySubClassWithSuperClassInNodeModules extends Subject {
    method mySuperclassMethod (line 6) | mySuperclassMethod() {

FILE: test/fixture/superclass-subclass/expected/two-classes.ts
  class Super (line 1) | class Super {
    method someMethod (line 4) | someMethod() {
  class Sub (line 10) | class Sub extends Super {
    method someMethod (line 13) | someMethod() {

FILE: test/fixture/superclass-subclass/input/another-sub-class.js
  class AnotherSubClass (line 3) | class AnotherSubClass extends DefaultExportClass {
    method constructor (line 4) | constructor() {

FILE: test/fixture/superclass-subclass/input/default-export-class.js
  class DefaultExportClass (line 1) | class DefaultExportClass {
    method constructor (line 3) | constructor() {

FILE: test/fixture/superclass-subclass/input/my-class.js
  class MyClass (line 3) | class MyClass extends MySuperClass {
    method constructor (line 5) | constructor() {
    method doSomething (line 12) | doSomething() {

FILE: test/fixture/superclass-subclass/input/my-sub-class.js
  class MySubClass (line 3) | class MySubClass extends MyClass {
    method constructor (line 4) | constructor() {

FILE: test/fixture/superclass-subclass/input/my-super-class.js
  class MySuperClass (line 1) | class MySuperClass {
    method constructor (line 3) | constructor() {
    method mySuperclassMethod (line 7) | mySuperclassMethod() {

FILE: test/fixture/superclass-subclass/input/superclass-in-node-modules.js
  class MySubClassWithSuperClassInNodeModules (line 3) | class MySubClassWithSuperClassInNodeModules extends Subject {
    method mySuperclassMethod (line 5) | mySuperclassMethod() {

FILE: test/fixture/superclass-subclass/input/two-classes.js
  class Super (line 1) | class Super {
    method someMethod (line 2) | someMethod() {
  class Sub (line 8) | class Sub extends Super {
    method someMethod (line 9) | someMethod() {

FILE: test/fixture/typescript-class/expected/declarations-in-superclass.ts
  class SuperTypeScriptClass (line 1) | class SuperTypeScriptClass {
  class SubTypeScriptClass (line 5) | class SubTypeScriptClass extends SuperTypeScriptClass {
    method constructor (line 8) | constructor() {
  class SubSubTypeScriptClass (line 15) | class SubSubTypeScriptClass extends SubTypeScriptClass {
    method constructor (line 16) | constructor() {

FILE: test/fixture/typescript-class/expected/typescript-class.ts
  class TypescriptClass (line 1) | class TypescriptClass {
    method constructor (line 4) | constructor() {

FILE: test/fixture/typescript-class/expected/typescript-sub-class.ts
  class TypescriptSubClass (line 3) | class TypescriptSubClass extends TypescriptClass {
    method constructor (line 6) | constructor() {

FILE: test/fixture/typescript-class/input/declarations-in-superclass.ts
  class SuperTypeScriptClass (line 1) | class SuperTypeScriptClass {
  class SubTypeScriptClass (line 5) | class SubTypeScriptClass extends SuperTypeScriptClass {
    method constructor (line 6) | constructor() {
  class SubSubTypeScriptClass (line 13) | class SubSubTypeScriptClass extends SubTypeScriptClass {
    method constructor (line 14) | constructor() {

FILE: test/fixture/typescript-class/input/typescript-class.ts
  class TypescriptClass (line 1) | class TypescriptClass {
    method constructor (line 4) | constructor() {

FILE: test/fixture/typescript-class/input/typescript-sub-class.ts
  class TypescriptSubClass (line 3) | class TypescriptSubClass extends TypescriptClass {
    method constructor (line 4) | constructor() {
Condensed preview — 87 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (119K chars).
[
  {
    "path": ".gitignore",
    "chars": 42,
    "preview": "/.idea\n/dist\n/node_modules\n/yarn-error.log"
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2018 Gregory Jacobs\n\nPermission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "README.md",
    "chars": 4308,
    "preview": "# js-to-ts-converter\n\nSmall utility that I wrote to script converting a JS codebase to TypeScript,\nwhile trying to solve"
  },
  {
    "path": "package.json",
    "chars": 1435,
    "preview": "{\n\t\"name\": \"js-to-ts-converter\",\n\t\"version\": \"0.18.2\",\n\t\"description\": \"Small utility to rename .js->.ts, and convert ES"
  },
  {
    "path": "src/cli.ts",
    "chars": 3733,
    "preview": "#!/usr/bin/env node\n\nimport * as path from \"path\";\nimport * as fs from 'fs';\n\nimport { IndentationText } from \"ts-morph\""
  },
  {
    "path": "src/converter/add-class-property-declarations/add-class-property-declarations.ts",
    "chars": 3020,
    "preview": "import { Project, ClassInstancePropertyTypes, PropertyDeclarationStructure, Scope } from \"ts-morph\";\nimport { parseJsCla"
  },
  {
    "path": "src/converter/add-class-property-declarations/correct-js-properties.ts",
    "chars": 5195,
    "preview": "import { JsClass } from \"./js-class\";\nimport { Graph, alg } from \"graphlib\";\nimport logger from \"../../logger/logger\";\n\n"
  },
  {
    "path": "src/converter/add-class-property-declarations/js-class.ts",
    "chars": 3475,
    "preview": "import { union } from \"../../util/set-utils\";\n\n/**\n * Represents a JavaScript class found in a source file.\n */\nexport c"
  },
  {
    "path": "src/converter/add-class-property-declarations/parse-js-classes.ts",
    "chars": 7950,
    "preview": "import { Project, ts, ClassDeclaration, ClassInstancePropertyTypes, MethodDeclaration, PropertyAccessExpression, SourceF"
  },
  {
    "path": "src/converter/add-class-property-declarations/parse-superclass-name-and-path.ts",
    "chars": 3577,
    "preview": "import { isValidIdentifier } from \"../../util/is-valid-identifier\";\nimport { ClassDeclaration, SourceFile } from \"ts-mor"
  },
  {
    "path": "src/converter/add-optionals-to-function-params.ts",
    "chars": 7492,
    "preview": "import { Project, CallExpression, ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, MethodDeclaration, NewE"
  },
  {
    "path": "src/converter/convert.ts",
    "chars": 3357,
    "preview": "import { Project, SyntaxKind } from \"ts-morph\";\nimport { addClassPropertyDeclarations } from \"./add-class-property-decla"
  },
  {
    "path": "src/converter/filter-out-node-modules.ts",
    "chars": 529,
    "preview": "import { Project } from \"ts-morph\";\n\n/**\n * Given a Project, removes all files that are under the node_modules folder.\n "
  },
  {
    "path": "src/create-ts-morph-project.ts",
    "chars": 1502,
    "preview": "import { Project, IndentationText } from \"ts-morph\";\nimport fastGlob from 'fast-glob';\n\n/**\n * Creates a ts-morph Projec"
  },
  {
    "path": "src/index.ts",
    "chars": 73,
    "preview": "export * from './js-to-ts-converter';\nexport * from './logger/log-level';"
  },
  {
    "path": "src/js-to-ts-converter.ts",
    "chars": 2961,
    "preview": "import * as path from 'path';\nimport { createTsMorphProject } from \"./create-ts-morph-project\";\nimport { convert } from "
  },
  {
    "path": "src/logger/index.ts",
    "chars": 109,
    "preview": "export * from './logger';\nexport * from './log-level';\n\nimport logger from './logger';\nexport default logger;"
  },
  {
    "path": "src/logger/log-level.ts",
    "chars": 146,
    "preview": "export type LogLevel = 'debug' | 'verbose' | 'info' | 'warn' | 'error';\nexport const logLevels = [ 'debug', 'verbose', '"
  },
  {
    "path": "src/logger/logger.ts",
    "chars": 1121,
    "preview": "import * as winston from 'winston';\nimport { LogLevel } from \"./log-level\";\n\nconst winstonLogger = winston.createLogger("
  },
  {
    "path": "src/util/find-import-for-identifier.ts",
    "chars": 1060,
    "preview": "import { ImportDeclaration, ImportSpecifier, SourceFile } from \"ts-morph\";\n\n/**\n * Finds an ImportDeclaration for a give"
  },
  {
    "path": "src/util/is-element-access-with-obj.ts",
    "chars": 1621,
    "preview": "import { ElementAccessExpression, Identifier, Node } from \"ts-morph\";\n\n/**\n * Determines if the given `node` is a Elemen"
  },
  {
    "path": "src/util/is-property-access-with-obj.ts",
    "chars": 1660,
    "preview": "import { Identifier, Node, PropertyAccessExpression } from \"ts-morph\";\n\n/**\n * Determines if the given `node` is a Prope"
  },
  {
    "path": "src/util/is-property-or-elemement-access-with-obj.ts",
    "chars": 1923,
    "preview": "import { ElementAccessExpression, Node, PropertyAccessExpression } from \"ts-morph\";\nimport { isPropertyAccessWithObj } f"
  },
  {
    "path": "src/util/is-this-referencing-var.ts",
    "chars": 781,
    "preview": "import { Node, SyntaxKind, VariableDeclaration } from \"ts-morph\";\n\n/**\n * Determines if the given AST Node is a Variable"
  },
  {
    "path": "src/util/is-valid-identifier.ts",
    "chars": 171,
    "preview": "/**\n * Helper to determine if a string of text is a valid JavaScript identifier.\n */\nexport function isValidIdentifier( "
  },
  {
    "path": "src/util/parse-destructured-props.ts",
    "chars": 656,
    "preview": "import { ts, SyntaxKind } from \"ts-morph\";\n\n/**\n * Given a ts.ObjectBindingPattern node, returns an array of the names t"
  },
  {
    "path": "src/util/set-utils.ts",
    "chars": 609,
    "preview": "/**\n * Unions two or more sets to create a combined set. Does not mutate the input\n * sets.\n */\nexport function union<T>"
  },
  {
    "path": "test/convert.spec.ts",
    "chars": 5973,
    "preview": "import { expect } from 'chai';\nimport { createTsMorphProject } from \"../src/create-ts-morph-project\";\nimport { convert }"
  },
  {
    "path": "test/fixture/class-with-this-constructor-reference/expected/my-class.ts",
    "chars": 118,
    "preview": "export class MyClass {\n\tpublic name: any;\n\n\tconstructor() {}\n\n\tmyMethod() {\n\t\tthis.name = this.constructor.name;\n\t}\n\n}"
  },
  {
    "path": "test/fixture/class-with-this-constructor-reference/input/my-class.js",
    "chars": 99,
    "preview": "export class MyClass {\n\n\tconstructor() {}\n\n\tmyMethod() {\n\t\tthis.name = this.constructor.name;\n\t}\n\n}"
  },
  {
    "path": "test/fixture/expression-extends/expected/expression-extends.ts",
    "chars": 136,
    "preview": "class ExpressionExtends extends Mixin.mix( SomeClass1, SomeClass2 ) {\n\tpublic someProp: any;\n\n\tconstructor() {\n\t\tthis.so"
  },
  {
    "path": "test/fixture/expression-extends/input/expression-extends.js",
    "chars": 112,
    "preview": "class ExpressionExtends extends Mixin.mix( SomeClass1, SomeClass2 ) {\n\tconstructor() {\n\t\tthis.someProp = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-exported-function.ts",
    "chars": 122,
    "preview": "import { myExportedFunction } from \"./exported-function\";\n\nmyExportedFunction();  // no args - should mark all as option"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-local-function-with-default-value.ts",
    "chars": 66,
    "preview": "function threeArg( arg1, arg2 = 1, arg3 = 2 ) {\n\n}\n\nthreeArg( 1 );"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-local-function.ts",
    "chars": 60,
    "preview": "function threeArg( arg1, arg2?, arg3? ) {\n\n}\n\nthreeArg( 1 );"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-sub-class-method.ts",
    "chars": 227,
    "preview": "import { SubClass } from \"./sub-class\";\n\nconst subClass = new SubClass();\n\nsubClass.subclassMethod( 1, 2 );  // should *"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-super-class-method.ts",
    "chars": 183,
    "preview": "import { SuperClass } from \"./super-class\";\n\nconst superclass = new SuperClass( 1 );  // marks arg2 and arg3 as optional"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/constructor-with-rest-param.ts",
    "chars": 263,
    "preview": "class ConstructorWithRestParam {\n\n\tconstructor( ...args ) {  // should *not* be marked as optional\n\n\t}\n\n\tmethodWithRestP"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/exported-function.ts",
    "chars": 119,
    "preview": "export function myExportedFunction( arg1?, arg2? ) {  // arg1 and arg2 made optional by call-to-exported-function.js\n\n}"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/sub-class.ts",
    "chars": 417,
    "preview": "import { SuperClass } from \"./super-class\";\n\nexport class SubClass extends SuperClass {\n\n\tsubclassMethod( arg1, arg2 ) {"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/super-class.ts",
    "chars": 327,
    "preview": "export class SuperClass {\n\n\tconstructor( arg1, arg2?, arg3? ) {  // arg2 and arg3 will be marked optional by call-to-sup"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/call-to-exported-function.js",
    "chars": 122,
    "preview": "import { myExportedFunction } from \"./exported-function\";\n\nmyExportedFunction();  // no args - should mark all as option"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/call-to-local-function-with-default-value.js",
    "chars": 66,
    "preview": "function threeArg( arg1, arg2 = 1, arg3 = 2 ) {\n\n}\n\nthreeArg( 1 );"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/call-to-local-function.js",
    "chars": 58,
    "preview": "function threeArg( arg1, arg2, arg3 ) {\n\n}\n\nthreeArg( 1 );"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/call-to-sub-class-method.js",
    "chars": 227,
    "preview": "import { SubClass } from \"./sub-class\";\n\nconst subClass = new SubClass();\n\nsubClass.subclassMethod( 1, 2 );  // should *"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/call-to-super-class-method.js",
    "chars": 183,
    "preview": "import { SuperClass } from \"./super-class\";\n\nconst superclass = new SuperClass( 1 );  // marks arg2 and arg3 as optional"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/constructor-with-rest-param.js",
    "chars": 263,
    "preview": "class ConstructorWithRestParam {\n\n\tconstructor( ...args ) {  // should *not* be marked as optional\n\n\t}\n\n\tmethodWithRestP"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/exported-function.js",
    "chars": 117,
    "preview": "export function myExportedFunction( arg1, arg2 ) {  // arg1 and arg2 made optional by call-to-exported-function.js\n\n}"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/sub-class.js",
    "chars": 415,
    "preview": "import { SuperClass } from \"./super-class\";\n\nexport class SubClass extends SuperClass {\n\n\tsubclassMethod( arg1, arg2 ) {"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/super-class.js",
    "chars": 323,
    "preview": "export class SuperClass {\n\n\tconstructor( arg1, arg2, arg3 ) {  // arg2 and arg3 will be marked optional by call-to-super"
  },
  {
    "path": "test/fixture/function-expressions-and-declarations/expected/class-with-function-expressions.ts",
    "chars": 1696,
    "preview": "class ClassWithFunctionExpressions {\n\tpublic destructured1: any;\n\tpublic destructured2: any;\n\tpublic prop1: any;\n\tpublic"
  },
  {
    "path": "test/fixture/function-expressions-and-declarations/input/class-with-function-expressions.js",
    "chars": 1549,
    "preview": "class ClassWithFunctionExpressions {\n\n\tmyMethod() {\n\t\tvar that = this;\n\n\t\tvar myFn1 = function() {\n\t\t\tthat.prop1 = 1;\n\t\t"
  },
  {
    "path": "test/fixture/include-exclude-patterns/expected/included/included-file.ts",
    "chars": 87,
    "preview": "class IncludedFile {\n\tpublic someProp: any;\n\n\tconstructor() {\n\t\tthis.someProp = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/include-exclude-patterns/input/included/excluded/excluded-file.js",
    "chars": 63,
    "preview": "class ExcludedFile {\n\tconstructor() {\n\t\tthis.someProp = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/include-exclude-patterns/input/included/included-file.js",
    "chars": 63,
    "preview": "class IncludedFile {\n\tconstructor() {\n\t\tthis.someProp = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/include-exclude-patterns/input/other-file-that-should-not-be-included.js",
    "chars": 41,
    "preview": "class OtherFileThatShouldNotBeIncluded {}"
  },
  {
    "path": "test/fixture/react-class-js/expected/react-class.tsx",
    "chars": 5132,
    "preview": "import * as React from \"react\";\nimport PropTypes from \"prop-types\";\nimport {\n  TableHead, TableRow, TableCell,\n  TableSo"
  },
  {
    "path": "test/fixture/react-class-js/input/react-class.js",
    "chars": 5116,
    "preview": "import * as React from \"react\";\nimport PropTypes from \"prop-types\";\nimport {\n  TableHead, TableRow, TableCell,\n  TableSo"
  },
  {
    "path": "test/fixture/react-class-jsx/expected/react-class.tsx",
    "chars": 5132,
    "preview": "import * as React from \"react\";\nimport PropTypes from \"prop-types\";\nimport {\n  TableHead, TableRow, TableCell,\n  TableSo"
  },
  {
    "path": "test/fixture/react-class-jsx/input/react-class.jsx",
    "chars": 5116,
    "preview": "import * as React from \"react\";\nimport PropTypes from \"prop-types\";\nimport {\n  TableHead, TableRow, TableCell,\n  TableSo"
  },
  {
    "path": "test/fixture/react-jsx-self-closing-element/expected/react-self-closing-element.tsx",
    "chars": 209,
    "preview": "import * as React from \"react\";\n\n/**\n * This example makes sure that .js is converted to .tsx when the only JSX\n * eleme"
  },
  {
    "path": "test/fixture/react-jsx-self-closing-element/input/react-self-closing-element.js",
    "chars": 209,
    "preview": "import * as React from \"react\";\n\n/**\n * This example makes sure that .js is converted to .tsx when the only JSX\n * eleme"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/another-sub-class.ts",
    "chars": 261,
    "preview": "import DefaultExportClass from \"./default-export-class\";\n\nexport class AnotherSubClass extends DefaultExportClass {\n\tpub"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/default-export-class.ts",
    "chars": 158,
    "preview": "class DefaultExportClass {\n\tpublic defaultExportClassProp: any;\n\n\tconstructor() {\n\t\tthis.defaultExportClassProp = 1;\n\t}\n"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/my-class.ts",
    "chars": 490,
    "preview": "import { MySuperClass } from './my-super-class';\n\nexport class MyClass extends MySuperClass {\n\tpublic myClassProp1: any;"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/my-sub-class.ts",
    "chars": 432,
    "preview": "import { MyClass } from \"./my-class\";\n\nexport class MySubClass extends MyClass {\n\tpublic mySubClassProp: any;\n\n\tconstruc"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/my-super-class.ts",
    "chars": 207,
    "preview": "export class MySuperClass {\n\tpublic mySuperClassProp: any;\n\n\tconstructor() {\n\t\tthis.mySuperclassMethod();  // should not"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/superclass-in-node-modules.ts",
    "chars": 173,
    "preview": "import { Subject } from 'rxjs';\n\nexport class MySubClassWithSuperClassInNodeModules extends Subject {\n\tpublic myProp: an"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/two-classes.ts",
    "chars": 195,
    "preview": "class Super {\n\tpublic superProp: any;\n\n\tsomeMethod() {\n\t\tthis.superProp = 1;\n\t}\n}\n\n\nclass Sub extends Super {\n\tpublic su"
  },
  {
    "path": "test/fixture/superclass-subclass/input/another-sub-class.js",
    "chars": 226,
    "preview": "import DefaultExportClass from \"./default-export-class\";\n\nexport class AnotherSubClass extends DefaultExportClass {\n\tcon"
  },
  {
    "path": "test/fixture/superclass-subclass/input/default-export-class.js",
    "chars": 121,
    "preview": "class DefaultExportClass {\n\n\tconstructor() {\n\t\tthis.defaultExportClassProp = 1;\n\t}\n\n}\n\nexport default DefaultExportClass"
  },
  {
    "path": "test/fixture/superclass-subclass/input/my-class.js",
    "chars": 409,
    "preview": "import { MySuperClass } from './my-super-class';\n\nexport class MyClass extends MySuperClass {\n\n\tconstructor() {\n\t\tthis.m"
  },
  {
    "path": "test/fixture/superclass-subclass/input/my-sub-class.js",
    "chars": 402,
    "preview": "import { MyClass } from \"./my-class\";\n\nexport class MySubClass extends MyClass {\n\tconstructor() {\n\t\tthis.mySuperClassPro"
  },
  {
    "path": "test/fixture/superclass-subclass/input/my-super-class.js",
    "chars": 176,
    "preview": "export class MySuperClass {\n\n\tconstructor() {\n\t\tthis.mySuperclassMethod();  // should not be added as a property\n\t}\n\n\tmy"
  },
  {
    "path": "test/fixture/superclass-subclass/input/package.json",
    "chars": 74,
    "preview": "{\n  \"devDependencies\": {},\n  \"dependencies\": {\n    \"rxjs\": \"^6.2.2\"\n  }\n}\n"
  },
  {
    "path": "test/fixture/superclass-subclass/input/superclass-in-node-modules.js",
    "chars": 152,
    "preview": "import { Subject } from 'rxjs';\n\nexport class MySubClassWithSuperClassInNodeModules extends Subject {\n\n\tmySuperclassMeth"
  },
  {
    "path": "test/fixture/superclass-subclass/input/two-classes.js",
    "chars": 147,
    "preview": "class Super {\n\tsomeMethod() {\n\t\tthis.superProp = 1;\n\t}\n}\n\n\nclass Sub extends Super {\n\tsomeMethod() {\n\t\tthis.superProp = "
  },
  {
    "path": "test/fixture/superclass-subclass-node-modules-not-installed/expected/superclass-in-node-modules.ts",
    "chars": 195,
    "preview": "import { SomeSuperclass } from 'some-not-installed-module';\n\nexport class MyClassWithSuperClassInNodeModules extends Som"
  },
  {
    "path": "test/fixture/superclass-subclass-node-modules-not-installed/input/superclass-in-node-modules.js",
    "chars": 174,
    "preview": "import { SomeSuperclass } from 'some-not-installed-module';\n\nexport class MyClassWithSuperClassInNodeModules extends Som"
  },
  {
    "path": "test/fixture/typescript-class/expected/declarations-in-superclass.ts",
    "chars": 611,
    "preview": "class SuperTypeScriptClass {\n\tpublic superProp: any;  // *declaration* that should not be added to subclass\n}\n\nclass Sub"
  },
  {
    "path": "test/fixture/typescript-class/expected/typescript-class.ts",
    "chars": 117,
    "preview": "export class TypescriptClass {\n\tpublic prop: any;  // shouldn't be duplicated\n\n\tconstructor() {\n\t\tthis.prop = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/typescript-class/expected/typescript-sub-class.ts",
    "chars": 184,
    "preview": "import { TypescriptClass } from \"./typescript-class\";\n\nexport class TypescriptSubClass extends TypescriptClass {\n\tpublic"
  },
  {
    "path": "test/fixture/typescript-class/input/declarations-in-superclass.ts",
    "chars": 588,
    "preview": "class SuperTypeScriptClass {\n\tpublic superProp: any;  // *declaration* that should not be added to subclass\n}\n\nclass Sub"
  },
  {
    "path": "test/fixture/typescript-class/input/typescript-class.ts",
    "chars": 117,
    "preview": "export class TypescriptClass {\n\tpublic prop: any;  // shouldn't be duplicated\n\n\tconstructor() {\n\t\tthis.prop = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/typescript-class/input/typescript-sub-class.ts",
    "chars": 163,
    "preview": "import { TypescriptClass } from \"./typescript-class\";\n\nexport class TypescriptSubClass extends TypescriptClass {\n\tconstr"
  },
  {
    "path": "test.ts",
    "chars": 894,
    "preview": "import { Project } from \"ts-morph\";\n\nconst tsAstProject = new Project();\nconst sourceFile = tsAstProject.createSourceFil"
  },
  {
    "path": "tsconfig.json",
    "chars": 4604,
    "preview": "{\n\t\"compilerOptions\": {\n\t\t/* Basic Options */\n\t\t\"target\": \"es2017\",                       /* Specify ECMAScript target v"
  }
]

About this extraction

This page contains the full source code of the gregjacobs/js-to-ts-converter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 87 files (102.8 KB), approximately 28.9k tokens, and a symbol index with 192 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!