[
  {
    "path": ".gitignore",
    "content": "/.idea\n/dist\n/node_modules\n/yarn-error.log"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Gregory Jacobs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# js-to-ts-converter\n\nSmall utility that I wrote to script converting a JS codebase to TypeScript,\nwhile trying to solve some of the common TypeScript errors that will be received\nupon such a conversion.\n\nThe utility performs the following transformations:\n\n1. Renames `.js` files to `.ts`\n2. Adds property declarations to ES6 classes so that they are compilable by the\n   TypeScript compiler (see below).\n3. Any function calls that provide fewer arguments than the declared parameters\n   in the function will cause the remaining parameters to be marked as optional\n   for that function. This solves TS errors like \"Expected 3 arguments, but \n   got 2\"\n   \nNote: because this utility utilizes the TypeScript Language Service to perform\nthe look-ups for #3, it may take a long time to run. For a small project, expect\na few minutes. For a larger project, it could take tens of minutes. Still much \nbetter than the days/weeks it could take to fix an entire codebase by hand :)\n \n\nFor #2 above, the utility basically looks at any `this` property accessed by a \nJS class, and fills in the appropriate TypeScript property declarations. Take \nthis `.js` input source file as an example:\n\n```js\nclass Super {\n\tsomeMethod() {\n\t\tthis.superProp = 1;\n\t}\n}\n\nclass Sub extends Super {\n\tsomeMethod() {\n\t\tthis.superProp = 2;\n\t\tthis.subProp = 2;\n\t}\n}\n```\n\n\nThe above JS classes are replaced with the following TS classes:\n\n```ts\nclass Super {\n    public superProp: any;   // <-- added\n\n    someMethod() {\n        this.superProp = 1;\n    }\n}\n\n\nclass Sub extends Super {\n    public subProp: any;    // <-- added\n\n    someMethod() {\n        this.superProp = 2;\n        this.subProp = 2;\n    }\n}\n```\n\nNote: properties used when `this` is assigned to another variable are also \nfound for purposes of creating property declarations. Example:\n\n```js\nclass MyClass {\n    myMethod() {\n        var that = this;\n        \n        that.something;  // <-- 'something' parsed as a class property\n    }\n}\n```\n\nFor #3 above, parameters are marked as 'optional' when there are callers that\ndon't provide all of them. For example, the following JavaScript:\n\n```js\nfunction myFunction( arg1, arg2 ) {\n\t// ...\n}\n\nmyFunction( 1 );  // only provide arg1\n```\n\nWill be transformed to the following TypeScript:\n\n```ts\nfunction myFunction( arg1, arg2? ) {  // <-- arg2 marked as optional\n\t// ...\n}\n\nmyFunction( 1 );\n```\n\n## Goal\n\nThe goal of this utility is to simply make the `.js` code compilable under the\nTypeScript compiler, so simply adding the property declarations typed as `any` \nwas the quickest option there. The utility may look at property initializers in \nthe future to determine a better type.\n\nIf you have other types of compiler errors that you think might be able to be \ntransformed by this utility, please feel free to raise an issue (or pull\nrequest!)\n\nHopefully you only need to use this utility once, but if it saved you time, \nplease star it so that I know it helped you out :)\n\n\n## Fair Warning\n\nThis utility makes modifications to the directory that you pass it. Make sure\nyou are in a clean git (or other VCS) state before running it in case you need\nto revert!\n\n\n## Running the Utility from the CLI\n\n```\nnpx js-to-ts-converter ./path/to/js/files\n```\n\nIf you would prefer to install the CLI globally, do this:\n\n```\nnpm install --global js-to-ts-converter\n\njs-to-ts-converter ./path/to/js/files\n```\n\n\n## Running the Utility from Node\n\nTypeScript: \n\n```ts\nimport { convertJsToTs, convertJsToTsSync } from 'js-to-ts-converter';\n\n\n// Async\nconvertJsToTs( 'path/to/js/files' ).then( \n    () => console.log( 'Done!' ),\n    ( err ) => console.log( 'Error: ', err );\n); \n\n\n// Sync\nconvertJsToTsSync( 'path/to/js/files' );\nconsole.log( 'Done!' );\n```\n\nJavaScript:\n\n```js\nconst { convertJsToTs, convertJsToTsSync } = require( 'js-to-ts-converter' );\n\n\n// Async\nconvertJsToTs( 'path/to/js/files' ).then( \n    () => console.log( 'Done!' ),\n    ( err ) => console.log( 'Error: ', err );\n); \n\n\n// Sync\nconvertJsToTsSync( 'path/to/js/files' );\nconsole.log( 'Done!' );\n```\n\n## Developing\n\nMake sure you have [Node.js](https://nodejs.org) installed. \n\nClone the git repo: \n\n```\ngit clone https://github.com/gregjacobs/js-to-ts-converter.git\n\ncd js-to-ts-converter\n```\n\nInstall dependencies:\n\n```\nnpm install\n```\n\nRun Tests:\n\n```\nnpm test\n```\n"
  },
  {
    "path": "package.json",
    "content": "{\n\t\"name\": \"js-to-ts-converter\",\n\t\"version\": \"0.18.2\",\n\t\"description\": \"Small utility to rename .js->.ts, and convert ES6 classes to TypeScript classes by filling in property declarations. See readme for more details.\",\n\t\"scripts\": {\n\t\t\"build\": \"tsc\",\n\t\t\"cli\": \"npm run build && node dist/cli.js\",\n\t\t\"test\": \"mocha --require ts-node/register --timeout 60000 --watch-extensions ts \\\"**/*.spec.ts\\\"\",\n\t\t\"prepublishOnly\": \"npm test && npm run build\"\n\t},\n\t\"keywords\": [\n\t\t\"typescript\",\n\t\t\"conversion\"\n\t],\n\t\"homepage\": \"https://github.com/gregjacobs/js-to-ts-converter\",\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"https://github.com/gregjacobs/js-to-ts-converter.git\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/gregjacobs/js-to-ts-converter/issues\"\n\t},\n\t\"author\": \"Gregory Jacobs <greg@greg-jacobs.com>\",\n\t\"license\": \"MIT\",\n\t\"main\": \"dist/index.js\",\n\t\"types\": \"dist/index.d.ts\",\n\t\"bin\": \"dist/cli.js\",\n\t\"files\": [\n\t\t\"dist/\"\n\t],\n\t\"dependencies\": {\n\t\t\"argparse\": \"^1.0.10\",\n\t\t\"fast-glob\": \"^3.2.4\",\n\t\t\"graphlib\": \"^2.1.5\",\n\t\t\"lodash\": \"^4.17.20\",\n\t\t\"resolve\": \"^1.8.1\",\n\t\t\"trace-error\": \"^0.0.7\",\n\t\t\"ts-morph\": \"^14.0.0\",\n\t\t\"typescript\": \"^4.6.2\",\n\t\t\"winston\": \"^3.0.0\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/chai\": \"^4.1.4\",\n\t\t\"@types/graphlib\": \"^2.1.4\",\n\t\t\"@types/lodash\": \"^4.14.112\",\n\t\t\"@types/mocha\": \"^5.2.4\",\n\t\t\"@types/node\": \"^10.5.2\",\n\t\t\"@types/winston\": \"^2.3.9\",\n\t\t\"chai\": \"^4.1.2\",\n\t\t\"mocha\": \"^5.2.0\",\n\t\t\"ts-node\": \"^7.0.0\"\n\t}\n}\n"
  },
  {
    "path": "src/cli.ts",
    "content": "#!/usr/bin/env node\n\nimport * as path from \"path\";\nimport * as fs from 'fs';\n\nimport { IndentationText } from \"ts-morph\";\nimport logger, { LogLevel, logLevels } from './logger';\nimport { convertJsToTsSync } from \"./js-to-ts-converter\";\n\n\nconst ArgumentParser = require('argparse').ArgumentParser;\nconst parser = new ArgumentParser( {\n\tversion: require( '../package.json' ).version,\n\taddHelp: true,\n\tdescription: 'JS -> TS Converter'\n} );\nparser.addArgument( 'directory', {\n\thelp: 'The directory of .js files to convert'\n} );\nparser.addArgument( '--indentation-text', {\n\thelp: 'How you would like new code to be indented',\n\tchoices: [ 'tab', 'twospaces', 'fourspaces', 'eightspaces' ],\n\tdefaultValue: 'tab'\n} );\nparser.addArgument( '--include', {\n\thelp: 'Glob patterns to include in the conversion. Separate multiple patterns ' +\n\t      'with a comma. The patterns must be valid for the \"glob-all\" npm ' +\n\t      'package (https://www.npmjs.com/package/glob-all), and are relative to ' +\n\t      'the input directory.\\n' +\n\t      'Example: --include=\"**/myFolder/**,**/*.js\"'\n} );\nparser.addArgument( '--exclude', {\n\thelp: 'Glob patterns to exclude from being converted. Separate multiple patterns ' +\n\t      'with a comma. The patterns must be valid for the \"glob-all\" npm ' +\n\t      'package (https://www.npmjs.com/package/glob-all), and are relative to ' +\n\t      'the input directory.\\n' +\n\t      'Example: --exclude=\"**/myFolder/**,**/*.jsx\"'\n} );\nparser.addArgument( '--log-level', {\n\thelp: `\n\t\tThe level of logs to print to the console. From highest amount of \\\n\t\tlogging to lowest amount of logging: '${logLevels.join(\"', '\")}' \n\t\tDefaults to verbose to tell you what's going on, as the script may take\n\t\ta long time to complete when looking up usages of functions. Use 'debug'\n\t\tto enable even more logging.\n\t`.trim().replace( /^\\t*/gm, '' ),\n\tchoices: logLevels,\n\tdefaultValue: 'verbose'\n} );\n\nconst args = parser.parseArgs();\nconst absolutePath = path.resolve( args.directory.replace( /\\/$/, '' ) );  // remove any trailing slash\n\n\nif( !fs.lstatSync( absolutePath ).isDirectory() ) {\n\tlogger.error( `${absolutePath} is not a directory. Please provide a directory` );\n\tprocess.exit( 1 );\n} else {\n\tlogger.info( `Processing directory: '${absolutePath}'` );\n}\n\n\nconvertJsToTsSync( absolutePath, {\n\tindentationText: resolveIndentationText( args.indentation_text ),\n\tlogLevel: resolveLogLevel( args.log_level ),\n\tincludePatterns: parseIncludePatterns( args.include ),\n\texcludePatterns: parseExcludePatterns( args.exclude )\n} );\n\n\n/**\n * Private helper to resolve the correct IndentationText enum from the CLI\n * 'indentation' argument.\n */\nfunction resolveIndentationText( indentationText: 'tab' | 'twospaces' | 'fourspaces' | 'eightspaces' ) {\n\tswitch( indentationText ) {\n\t\tcase 'tab'         : return IndentationText.Tab;\n\t\tcase 'twospaces'   : return IndentationText.TwoSpaces;\n\t\tcase 'fourspaces'  : return IndentationText.FourSpaces;\n\t\tcase 'eightspaces' : return IndentationText.EightSpaces;\n\n\t\tdefault : return IndentationText.Tab;\n\t}\n}\n\n\nfunction resolveLogLevel( logLevelStr: string ): LogLevel {\n\tif( !logLevels.includes( logLevelStr ) ) {\n\t\tthrow new Error( `\n\t\t\tUnknown --log-level argument '${logLevelStr}'\n\t\t\tMust be one of: '${logLevels.join( \"', '\" )}'\n\t\t`.trim().replace( /\\t*/gm, '' ) );\n\t}\n\n\treturn logLevelStr as LogLevel;\n}\n\n\nfunction parseIncludePatterns( includePatterns: string | undefined ): string[] | undefined {\n\tif( !includePatterns ) { return undefined; }  // return undefined to use the default\n\n\treturn includePatterns.split( ',' );\n}\n\nfunction parseExcludePatterns( excludePatterns: string | undefined ): string[] {\n\tif( !excludePatterns ) { return []; }\n\n\treturn excludePatterns.split( ',' );\n}"
  },
  {
    "path": "src/converter/add-class-property-declarations/add-class-property-declarations.ts",
    "content": "import { Project, ClassInstancePropertyTypes, PropertyDeclarationStructure, Scope } from \"ts-morph\";\nimport { parseJsClasses } from \"./parse-js-classes\";\nimport { correctJsProperties } from \"./correct-js-properties\";\nimport logger from \"../../logger/logger\";\n\n/**\n * Parses all source files looking for ES6 classes, and takes any `this`\n * property access to create a PropertyDeclaration for the class.\n *\n * For example:\n *\n *     class Something {\n *         constructor() {\n *             this.someProp = 1;\n *         }\n *     }\n *\n * Is changed to:\n *\n *     class Something {\n *         someProp: any;\n *\n *         constructor() {\n *             this.someProp = 1;\n *         }\n *     }\n */\nexport function addClassPropertyDeclarations( tsAstProject: Project ): Project {\n\t// Parse the JS classes for all of the this.xyz properties that they use\n\tconst jsClasses = parseJsClasses( tsAstProject );\n\n\t// Correct the JS classes' properties for superclass/subclass relationships\n\t// (essentially remove properties from subclasses that are defined by their\n\t// superclasses)\n\tconst propertiesCorrectedJsClasses = correctJsProperties( jsClasses );\n\n\t// Fill in field definitions for each of the classes\n\tpropertiesCorrectedJsClasses.forEach( jsClass => {\n\t\tconst sourceFile = tsAstProject.getSourceFileOrThrow( jsClass.path );\n\t\tlogger.verbose( `  Updating class '${jsClass.name}' in '${sourceFile.getFilePath()}'` );\n\n\t\tconst classDeclaration = sourceFile.getClassOrThrow( jsClass.name! );\n\t\tconst jsClassProperties = jsClass.properties;\n\n\t\t// If the utility was run against a TypeScript codebase, we should not\n\t\t// fill in property declarations for properties that are already\n\t\t// declared in the class. However, we *should* fill in any missing\n\t\t// declarations. Removing any already-declared declarations from the\n\t\t// jsClassProperties.\n\t\tconst currentPropertyDeclarations = classDeclaration.getInstanceProperties()\n\t\t\t.reduce( ( props: Set<string>, prop: ClassInstancePropertyTypes ) => {\n\t\t\t\tconst propName = prop.getName();\n\t\t\t\treturn propName ? props.add( propName ) : props;\n\t\t\t}, new Set<string>() );\n\n\t\tlet undeclaredProperties = [ ...jsClassProperties ]\n\t\t\t.filter( ( propName: string ) => !currentPropertyDeclarations.has( propName ) );\n\n\t\t// If the utility found a reference to this.constructor, we don't want to\n\t\t// add a property called 'constructor'. Filter that out now.\n\t\t// https://github.com/gregjacobs/js-to-ts-converter/issues/9\n\t\tundeclaredProperties = undeclaredProperties\n\t\t\t.filter( ( propName: string ) => propName !== 'constructor' );\n\n\t\t// Add all currently-undeclared properties\n\t\tconst propertyDeclarations = undeclaredProperties.map( propertyName => {\n\t\t\treturn {\n\t\t\t\tname: propertyName,\n\t\t\t\ttype: 'any',\n\t\t\t\tscope: Scope.Public\n\t\t\t} as PropertyDeclarationStructure;\n\t\t} );\n\n\t\tlogger.verbose( `    Adding property declarations for properties: '${undeclaredProperties.join( \"', '\" )}'` );\n\t\tclassDeclaration.insertProperties( 0, propertyDeclarations );\n\t} );\n\n\treturn tsAstProject;\n}"
  },
  {
    "path": "src/converter/add-class-property-declarations/correct-js-properties.ts",
    "content": "import { JsClass } from \"./js-class\";\nimport { Graph, alg } from \"graphlib\";\nimport logger from \"../../logger/logger\";\n\n/**\n * After the graph of original {@link JsClass}es and their properties have been\n * created, we need to remove properties from subclasses that are defined in\n * their superclasses.\n *\n * This function takes the original graph of classes with all properties in each\n * class and returns a new list of JsClasses with the properties properly\n * filtered so that subclasses do not define the same properties that are\n * already present in their superclasses.\n *\n * ## Algorithm\n *\n * 1. Build graph of subclasses -> superclasses\n * 2. Take topological sort of graph\n * 3. Starting at the superclasses in the sort, fill in the\n *    propertySets for each JsClass. For every subclass encountered,\n *    filter out its superclass properties to create the subclass's property\n *    set\n * 4. Use the propertySets to create a new list of JsClasses\n */\nexport function correctJsProperties( jsClasses: JsClass[] ): JsClass[] {\n\tlogger.debug( 'Building graph of class hierarchy to determine which class properties belong to superclasses/subclasses' );\n\n\tconst jsClassHierarchyGraph = new Graph();\n\n\t// First, add all nodes to the graph\n\tjsClasses.forEach( jsClass => {\n\t\tjsClassHierarchyGraph.setNode( jsClass.id, jsClass );\n\t} );\n\n\t// Second, connect the subclasses to superclasses in the graph\n\tjsClasses.forEach( jsClass => {\n\t\tif( jsClass.superclassId ) {\n\t\t\t// If we come across a JsClass whose superclass is in the node_modules\n\t\t\t// directory (i.e. imported from another package), do not try to\n\t\t\t// go into that package. We're not going to try to understand an ES5\n\t\t\t// module\n\t\t\tif( jsClass.isSuperclassInNodeModules() ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// As a bit of error checking, make sure that we're not going to\n\t\t\t// accidentally create a graph node by adding an edge to\n\t\t\t// jsClass.superclassId. This would happen if we didn't figure out\n\t\t\t// the correct path to the superclass in the parse phase, or we\n\t\t\t// didn't have the superclass's source file added to the project.\n\t\t\tif( !jsClassHierarchyGraph.hasNode( jsClass.superclassId ) ) {\n\t\t\t\tthrow new Error( `\n\t\t\t\t\tAn error occurred while adding property declarations to class\n\t\t\t\t\t'${jsClass.name}' in file:\n\t\t\t\t\t    '${jsClass.path}'\n\t\t\t\t\t\n\t\t\t\t\tDid not parse this class's superclass ('${jsClass.superclassName}') from file:\n\t\t\t\t\t    '${jsClass.superclassPath}'\n\t\t\t\t\tduring the parse phase. \n\t\t\t\t\t\n\t\t\t\t\tMake sure that this class's superclass's .js file is within the \n\t\t\t\t\tdirectory passed to this conversion utility, or otherwise \n\t\t\t\t\tthere is a bug in this utility. Please report at:\n\t\t\t\t\t    https://github.com/gregjacobs/js-to-ts-converter/issues\n\t\t\t\t\t \n\t\t\t\t\tDebugging info:\n\t\t\t\t\t\n\t\t\t\t\tThis class's graph ID: ${jsClass.id}\n\t\t\t\t\tIt's superclass's graph ID: ${jsClass.superclassId}\n\t\t\t\t\t\n\t\t\t\t\tCurrent IDs in the graph:\n\t\t\t\t\t    ${jsClassHierarchyGraph.nodes().join( '\\n    ' )}\n\t\t\t\t`.replace( /^\\t*/gm, '' ) );\n\t\t\t}\n\n\t\t\tjsClassHierarchyGraph.setEdge( jsClass.id, jsClass.superclassId );\n\t\t}\n\t} );\n\n\t// the topological sort is going to put superclasses later in the returned\n\t// array, so reverse it\n\tlogger.debug( 'Topologically sorting the graph in superclass->subclass order' );\n\tconst superclassToSubclassOrder = alg.topsort( jsClassHierarchyGraph ).reverse();\n\n\t// Starting from superclass JsClass instances and walking down to subclass\n\t// JsClass instances, fill in the property sets. When a subclass is\n\t// encountered, take all of the properties that were used in that subclass,\n\t// minus the properties in its superclass, in order to determine the\n\t// subclass-specific properties\n\tsuperclassToSubclassOrder.forEach( jsClassId => {\n\t\tconst jsClass = jsClassHierarchyGraph.node( jsClassId ) as JsClass;\n\t\tconst subclassOnlyProperties = new Set<string>( jsClass.properties );\n\n\t\tconst superclasses = getSuperclasses( jsClass );\n\t\tsuperclasses.forEach( ( superclass: JsClass ) => {\n\t\t\t// Filter out both properties and methods from each superclass\n\t\t\tsuperclass.members.forEach( ( superclassProp: string ) => {\n\t\t\t\tsubclassOnlyProperties.delete( superclassProp );\n\t\t\t} );\n\t\t} );\n\n\t\tconst newJsClass = new JsClass( {\n\t\t\tpath: jsClass.path,\n\t\t\tname: jsClass.name,\n\t\t\tsuperclassName: jsClass.superclassName,\n\t\t\tsuperclassPath: jsClass.superclassPath,\n\t\t\tmethods: jsClass.methods,\n\t\t\tproperties: subclassOnlyProperties\n\t\t} );\n\n\t\t// Re-assign the new JsClass with the correct subclass properties back\n\t\t// to the graph for the next iteration, in case there is a subclass of\n\t\t// the current class which needs to read those properties\n\t\tjsClassHierarchyGraph.setNode( jsClassId, newJsClass );\n\t} );\n\n\n\t// Return all of the new JsClass instances with properties corrected for\n\t// superclass/subclasses\n\treturn jsClassHierarchyGraph.nodes()\n\t\t.map( jsClassId => jsClassHierarchyGraph.node( jsClassId ) as JsClass );\n\n\n\tfunction getSuperclasses( jsClass: JsClass ) {\n\t\tconst superclasses: JsClass[] = [];\n\n\t\twhile( jsClass.superclassId ) {\n\t\t\tconst superclass = jsClassHierarchyGraph.node( jsClass.superclassId ) as JsClass;\n\t\t\tsuperclasses.push( superclass );\n\n\t\t\tjsClass = superclass;\n\t\t}\n\t\treturn superclasses;\n\t}\n}"
  },
  {
    "path": "src/converter/add-class-property-declarations/js-class.ts",
    "content": "import { union } from \"../../util/set-utils\";\n\n/**\n * Represents a JavaScript class found in a source file.\n */\nexport class JsClass {\n\t/**\n\t * The name of the class.\n\t *\n\t * Will be undefined for a default export class.\n\t */\n\tpublic readonly name: string | undefined;\n\n\t/**\n\t * The absolute path of the file that the class was found in.\n\t */\n\tpublic readonly path: string;\n\n\t/**\n\t * The name of this class's superclass. Will be `undefined` if the class\n\t * does not have a superclass.\n\t */\n\tpublic readonly superclassName: string | undefined;\n\n\t/**\n\t * The path to the file which holds this class's superclass. If the same\n\t * file that holds this class also holds its superclass, this will be the\n\t * same value as the {@link #path}.\n\t *\n\t * Will be `undefined` if the superclass was found in the node_modules\n\t * folder. We don't try to resolve the path of a module that exists in the\n\t * node_modules folder as they're not relevant to this conversion utility,\n\t * and we want to allow conversions of codebases that don't have\n\t * node_modules installed (which can really improve performance as\n\t * ts-morph doesn't try to resolve them when it finds imports in .ts\n\t * files)\n\t */\n\tpublic readonly superclassPath: string | undefined;\n\n\t/**\n\t * The set of methods found in the class.\n\t */\n\tpublic readonly methods: Set<string>;\n\n\t/**\n\t * The set of properties found to be used in the class. These are inferred\n\t * from usages. For example: console.log(this.something) would tell us that\n\t * the class has a property `something`\n\t */\n\tpublic readonly properties: Set<string>;\n\n\t/**\n\t * A union of the {@link #methods} and {@link #properties} sets\n\t */\n\tpublic readonly members: Set<string>;\n\n\tconstructor( cfg: {\n\t\tname: string | undefined;\n\t\tpath: string;\n\t\tsuperclassName: string | undefined,\n\t\tsuperclassPath: string | undefined,\n\t\tmethods?: Set<string>;\n\t\tproperties?: Set<string>;\n\t} ) {\n\t\tthis.name = cfg.name;\n\t\tthis.path = cfg.path;\n\t\tthis.superclassName = cfg.superclassName;\n\t\tthis.superclassPath = cfg.superclassPath;\n\t\tthis.methods = cfg.methods || new Set<string>();\n\t\tthis.properties = cfg.properties || new Set<string>();\n\n\t\tthis.members = union( this.methods, this.properties );\n\t}\n\n\t/**\n\t * String identifier for the JsClass which is a combination of its file path\n\t * and class name. Used to store JsClass nodes on a graphlib Graph.\n\t */\n\tpublic get id(): string {\n\t\treturn `${this.path}_${this.name}`;\n\t}\n\n\t/**\n\t * Retrieves the ID of the superclass JsClass instance, if the JsClass has\n\t * one. If not, returns undefined.\n\t *\n\t * Also returns `undefined` if the class is found to be in the node_modules\n\t * folder, as we don't want to attempt to parse ES5 modules.\n\t */\n\tpublic get superclassId(): string | undefined {\n\t\tif( this.isSuperclassInNodeModules() ) {\n\t\t\t// If the superclass is in the node_modules folder, we'll\n\t\t\t// essentially treat this JsClass as if it didn't have a superclass.\n\t\t\t// See `isSuperclassInNodeModules()` jsdoc for details.\n\t\t\treturn undefined;\n\n\t\t} else {\n\t\t\treturn this.superclassName && `${this.superclassPath}_${this.superclassName}`;\n\t\t}\n\t}\n\n\n\t/**\n\t * Determines if the JsClass's superclass was found in the node_modules\n\t * directory (i.e. it extends from another package).\n\t *\n\t * If so, we're not going to try to understand a possibly ES5 module for\n\t * its properties, so we'll just stop processing at that point.\n\t */\n\tpublic isSuperclassInNodeModules(): boolean {\n\t\treturn this.superclassPath === undefined;\n\t}\n\n}"
  },
  {
    "path": "src/converter/add-class-property-declarations/parse-js-classes.ts",
    "content": "import { Project, ts, ClassDeclaration, ClassInstancePropertyTypes, MethodDeclaration, PropertyAccessExpression, SourceFile, SyntaxKind, VariableDeclaration } from \"ts-morph\";\nimport { JsClass } from \"./js-class\";\nimport { difference, union } from \"../../util/set-utils\";\nimport { parseDestructuredProps } from \"../../util/parse-destructured-props\";\nimport { parseSuperclassNameAndPath } from \"./parse-superclass-name-and-path\";\nimport { isThisReferencingVar } from \"../../util/is-this-referencing-var\";\nimport { propertyAccessWithObjFilter } from \"../../util/is-property-access-with-obj\";\nimport logger from \"../../logger/logger\";\n\n/**\n * Parses the classes out of each .js file in the SourceFilesCollection, and\n * forms a tree representing their hierarchy.\n *\n * ## Description of algorithm:\n *\n * Each source file is parsed to find all file-level classes. Their superclasses\n * and import paths for those superclasses are also recorded to form an\n * adjacency list graph of classes keyed by their file path.\n *\n * Each class is also processed to find and record any property accesses of the\n * `this` object. For instance, in the following class, there are 3\n * PropertyAccessExpressions that pull from the `this` object ('something1',\n * 'something2', and 'something3'):\n *\n *     class Something {\n *         constructor() {\n *             this.something1 = 1;\n *             this.something2 = 2;\n *         }\n *\n *         someMethod() {\n *             console.log( this.something3 );\n *\n *             console.log( window.location );  // <-- not a `this` PropertyAccessExpression\n *         }\n *     }\n *\n * The returned graph will be used later to determine which TS class property\n * definitions should be placed in superclasses vs. subclasses. Properties used\n * by a superclass and a subclass should only be defined in the superclass.\n */\nexport function parseJsClasses( tsAstProject: Project ): JsClass[] {\n\tlogger.verbose( \"Parsing JS classes in the codebase...\" );\n\tconst files = tsAstProject.getSourceFiles();\n\n\tconst jsClasses = files.reduce( ( classes: JsClass[], file: SourceFile ) => {\n\t\tlogger.debug( `Parsing classes in file: ${file.getFilePath()}` );\n\n\t\tconst fileClasses = parseFileClasses( file );\n\t\treturn classes.concat( fileClasses );\n\t}, [] );\n\n\treturn jsClasses;\n}\n\n\n/**\n * Parses the file-level classes out of the given `sourceFile`.\n */\nfunction parseFileClasses( sourceFile: SourceFile ): JsClass[] {\n\treturn sourceFile.getClasses().map( fileClass => {\n\t\tconst className = fileClass.getName();\n\n\t\tlogger.debug( `  Parsing class: ${className}` );\n\n\t\tconst { superclassName, superclassPath } = parseSuperclassNameAndPath( sourceFile, fileClass );\n\t\tconst methodNames = getMethodNames( fileClass );\n\t\tconst propertyNames = getPropertyNames( fileClass );\n\t\tconst propertiesMinusMethods = difference( propertyNames, methodNames );  // remove any method names from this Set\n\n\t\treturn new JsClass( {\n\t\t\tpath: sourceFile.getFilePath(),\n\t\t\tname: className,\n\t\t\tsuperclassName,\n\t\t\tsuperclassPath,\n\t\t\tmethods: methodNames,\n\t\t\tproperties: propertiesMinusMethods\n\t\t} );\n\t} );\n}\n\n\n/**\n * Parses the method names from the class into a Set of strings.\n */\nfunction getMethodNames( fileClass: ClassDeclaration ): Set<string> {\n\treturn fileClass.getMethods()\n\t\t.reduce( ( methods: Set<string>, method: MethodDeclaration ) => {\n\t\t\treturn methods.add( method.getName() );\n\t\t}, new Set<string>() );\n}\n\n\n/**\n * Retrieves the list of propertyNames used in the class. This may also include\n * method names (which are technically properties), which we'll filter out later.\n */\nfunction getPropertyNames( fileClass: ClassDeclaration ) {\n\tconst existingPropertyDeclarations = parsePropertyDeclarations( fileClass );  // in case we are actually parsing a TypeScript class with existing declarations\n\tconst propertyAccesses = parsePropertyAccesses( fileClass );\n\tconst destructuringUsesOfProperties = parseDestructuringThisAssignments( fileClass );\n\tconst propertyAccessesOfThisAssignedVars = parsePropertyAccessesOfThisAssignedVars( fileClass );\n\n\treturn union(\n\t\texistingPropertyDeclarations,\n\t\tpropertyAccesses,\n\t\tdestructuringUsesOfProperties,\n\t\tpropertyAccessesOfThisAssignedVars\n\t);\n}\n\n\n/**\n * In the case that the utility is actually parsing TypeScript classes with\n * existing property declarations, we want to know about these so we don't\n * accidentally write in new ones of the same name.\n */\nfunction parsePropertyDeclarations( fileClass: ClassDeclaration ): Set<string> {\n\treturn fileClass.getInstanceProperties()\n\t\t.reduce( ( props: Set<string>, prop: ClassInstancePropertyTypes ) => {\n\t\t\tconst propName = prop.getName();\n\t\t\treturn 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)\n\t\t}, new Set<string>() );\n}\n\n\n/**\n * Parses the property names of `this` PropertyAccessExpressions.\n *\n * Examples:\n *\n *     this.something = 42;\n *     console.log( this.something2 );\n *\n *     const { destructured1, destructured2 } = this;\n *\n * Method returns:\n *\n *    Set( [ 'something', 'something2', 'destructured1', 'destructured2' ] )\n */\nfunction parsePropertyAccesses( fileClass: ClassDeclaration ): Set<string> {\n\t// First, find all of the `this.something` properties\n\tconst thisProps = fileClass\n\t\t.getDescendantsOfKind( SyntaxKind.PropertyAccessExpression )\n\t\t.filter( ( prop: PropertyAccessExpression ) => prop.getExpression().getKind() === SyntaxKind.ThisKeyword );\n\n\tconst propNamesSet = thisProps\n\t\t.reduce( ( props: Set<string>, prop: PropertyAccessExpression ) => {\n\t\t\treturn props.add( prop.getName() );\n\t\t}, new Set<string>() );\n\n\treturn propNamesSet;\n}\n\n\n/**\n * Parses any object destructuring statements of the form:\n *\n *     var { a, b } = this;\n *\n * And returns Set( [ 'a', 'b' ] ) in this case.\n */\nfunction parseDestructuringThisAssignments( fileClass: ClassDeclaration ): Set<string> {\n\t// Second, find any `var { a, b } = this` statements\n\tconst destructuredProps = fileClass\n\t\t.getDescendantsOfKind( SyntaxKind.VariableDeclaration )\n\t\t.filter( ( varDec: VariableDeclaration ) => {\n\t\t\treturn varDec.compilerNode.name.kind === SyntaxKind.ObjectBindingPattern;\n\t\t} );\n\n\treturn destructuredProps\n\t\t.reduce( ( propNames: Set<string>, varDec: VariableDeclaration ) => {\n\t\t\tconst destructuredPropNames = parseDestructuredProps( varDec.compilerNode.name as ts.ObjectBindingPattern );\n\t\t\tdestructuredPropNames.forEach( propName => propNames.add( propName ) );\n\n\t\t\treturn propNames;\n\t\t}, new Set<string>() );\n}\n\n\n/**\n * Parses property accesses of variables that are assigned to the `this`\n * keyword.\n *\n * For example:\n *\n *     var that = this;\n *\n *     that.someProp1 = 1;\n *     that.someProp2 = 2;\n *\n * In the above code, the Set( [ 'someProp1', 'someProp2' ] ) is returned\n */\nfunction parsePropertyAccessesOfThisAssignedVars(\n\tfileClass: ClassDeclaration\n): Set<string> {\n\tconst methods = fileClass.getMethods();\n\n\treturn methods.reduce( ( propNames: Set<string>, method: MethodDeclaration ) => {\n\t\tconst thisVarDeclarations = method\n\t\t\t.getDescendantsOfKind( SyntaxKind.VariableDeclaration )\n\t\t\t.filter( isThisReferencingVar );\n\n\t\t// Get the array of identifiers assigned to `this`. Ex: [ 'that', 'self' ]\n\t\tconst thisVarIdentifiers = thisVarDeclarations\n\t\t\t.map( ( thisVarDec: VariableDeclaration ) => thisVarDec.getName() );\n\n\t\tthisVarIdentifiers.forEach( ( thisVarIdentifier: string ) => {\n\t\t\t// Get the properties accessed from the `this` identifiers (i.e. from\n\t\t\t// 'that', 'self', etc.)\n\t\t\tconst propNamesAccessedFromIdentifier = method\n\t\t\t\t.getDescendantsOfKind( SyntaxKind.PropertyAccessExpression )\n\t\t\t\t.filter( propertyAccessWithObjFilter( thisVarIdentifier ) )\n\t\t\t\t.map( ( p: PropertyAccessExpression ) => p.getName() );\n\n\t\t\tpropNamesAccessedFromIdentifier\n\t\t\t\t.forEach( ( propName: string ) => propNames.add( propName ) );\n\t\t} );\n\n\t\treturn propNames;\n\t}, new Set<string>() );\n}"
  },
  {
    "path": "src/converter/add-class-property-declarations/parse-superclass-name-and-path.ts",
    "content": "import { isValidIdentifier } from \"../../util/is-valid-identifier\";\nimport { ClassDeclaration, SourceFile } from \"ts-morph\";\nimport { findImportForIdentifier } from \"../../util/find-import-for-identifier\";\nconst resolve = require( 'resolve' );\nconst TraceError = require( 'trace-error' );\n\n/**\n * Given a file and ClassDeclaration, finds the name of the superclass and the\n * full path to the module (file) that hosts the superclass.\n *\n * `superclass` and `superclassPath` in the return object will be `null` if\n * there is no superclass.\n */\nexport function parseSuperclassNameAndPath(\n\tfile: SourceFile,\n\tfileClass: ClassDeclaration\n): {\n\tsuperclassName: string | undefined;\n\tsuperclassPath: string | undefined;\n} {\n\tlet superclassName: string | undefined;\n\tlet superclassPath: string | undefined;\n\n\tconst heritage = fileClass.getExtends();\n\tif( heritage ) {\n\t\tsuperclassName = heritage.getExpression().getText();\n\n\t\t// Confirm that the superclass is an identifier rather than an\n\t\t// expression. It would be a bit much to try to understand expressions\n\t\t// as a class's 'extends', so just ignore these for now.\n\t\t// Example of ignored class extends:\n\t\t//\n\t\t//    class MyClass extends Mixin.mix( MixinClass1, MixinClass2 )\n\t\t//\n\t\tif( !isValidIdentifier( superclassName ) ) {\n\t\t\tsuperclassName = undefined;  // superclass was not a valid identifier\n\n\t\t} else if( !!file.getClass( superclassName ) ) {\n\t\t\tsuperclassPath = file.getFilePath();\n\n\t\t} else {\n\t\t\tsuperclassPath = findImportPathForIdentifier( file, superclassName );\n\t\t}\n\t}\n\n\treturn {\n\t\tsuperclassName,\n\t\tsuperclassPath: superclassPath && superclassPath.replace( /\\\\/g, '/' )  // normalize backslashes on Windows to forward slashes so we can compare directories with the paths that ts-morph produces\n\t};\n}\n\n\n/**\n * Finds the absolute path for the import with the given `identifier`.\n *\n * For example, if we were looking for the identifier 'MyClass' in the following\n * list of imports:\n *\n *     import { Something } from './somewhere';\n *     import { MyClass } from './my-class';\n *\n * Then the method would return '/absolute/path/to/my-class.js';\n *\n * If there is no import for `identifier`, the method returns `undefined`.\n */\nfunction findImportPathForIdentifier(\n\tsourceFile: SourceFile,\n\tidentifier: string\n): string | undefined {\n\tconst importWithIdentifier = findImportForIdentifier( sourceFile, identifier );\n\n\tif( importWithIdentifier ) {\n\t\tconst moduleSpecifier = importWithIdentifier.getModuleSpecifier().getLiteralValue();\n\n\t\tif( !moduleSpecifier.startsWith( '.' ) ) {\n\t\t\t// if the import path isn't relative (i.e. doesn't start with './'\n\t\t\t// or '../'), then it must be in node_modules. Return `undefined` to\n\t\t\t// represent that. We don't want to parse node_modules, and we\n\t\t\t// should be able to migrate the codebase without node_modules even\n\t\t\t// being installed.\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// If it's a relative import, return the absolute path to the module,\n\t\t// based on the source file that the import was found\n\t\tconst basedir = sourceFile.getDirectoryPath();\n\t\ttry {\n\t\t\treturn resolve.sync( moduleSpecifier, {\n\t\t\t\tbasedir,\n\t\t\t\textensions: [ '.ts', '.js' ]\n\t\t\t} );\n\n\t\t} catch( error ) {\n\t\t\tthrow new TraceError( `\n\t\t\t\tAn error occurred while trying to resolve the absolute path to\n\t\t\t\tthe import of identifier '${identifier}' in source file:\n\t\t\t\t    '${sourceFile.getFilePath()}'\n\t\t\t\t    \n\t\t\t\tWas looking at the import with text:\n\t\t\t\t    ${importWithIdentifier.getText()}   \n\t\t\t`.trim().replace( /^\\t*/gm, '' ), error );\n\t\t}\n\t}\n\n\t// Nothing found, return undefined\n\treturn undefined;\n}"
  },
  {
    "path": "src/converter/add-optionals-to-function-params.ts",
    "content": "import { Project, CallExpression, ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, MethodDeclaration, NewExpression, Node, SourceFile, SyntaxKind } from \"ts-morph\";\nimport logger from \"../logger/logger\";\n\ntype NameableFunction = FunctionDeclaration | MethodDeclaration;\ntype FunctionTransformTarget = NameableFunction | ConstructorDeclaration;\n\n/**\n * Adds the question token to function/method/constructor parameters that are\n * deemed to be optional based on the calls to that function/method/constructor\n * in the codebase.\n *\n * For example, if we have:\n *\n *     function myFn( arg1, arg2, arg3 ) {\n *         // ...\n *     }\n *\n *     myFn( 1, 2, 3 );  // all 3 args provided\n *     myFn( 1, 2 );     // <-- a call site only provides two arguments\n *\n * Then the resulting TypeScript function will be:\n *\n *     function myFn( arg1, arg2, arg3? ) {   // <-- arg3 marked as optional\n *         // ...\n *     }\n *\n * Note: Just calling the language service to look up references takes a lot of\n * time. Might have to optimize this somehow in the future.\n */\nexport function addOptionalsToFunctionParams( tsAstProject: Project ): Project {\n\tlogger.verbose( 'Beginning routine to mark function parameters as optional when calls exist that supply fewer args than parameters...' );\n\tconst sourceFiles = tsAstProject.getSourceFiles();\n\n\tlogger.verbose( 'Parsing function/method/constructor calls from codebase.' );\n\tconst constructorMinArgsMap = parseClassConstructorCalls( sourceFiles );\n\tconst functionsMinArgsMap = parseFunctionAndMethodCalls( sourceFiles );\n\n\tlogger.verbose( 'Marking parameters as optional' );\n\taddOptionals( constructorMinArgsMap );\n\taddOptionals( functionsMinArgsMap );\n\n\treturn tsAstProject;\n}\n\n\n/**\n * Finds the call sites of each ClassDeclaration's constructor in order to\n * determine if any of its parameters should be marked as optional.\n *\n * Returns a Map keyed by ClassDeclaration which contains the minimum number of\n * arguments passed to that class's constructor.\n *\n * Actually marking the parameters as optional is done in a separate phase.\n */\nfunction parseClassConstructorCalls( sourceFiles: SourceFile[] ): Map<ConstructorDeclaration, number> {\n\tlogger.verbose( 'Finding all calls to class constructors...' );\n\tconst constructorMinArgsMap = new Map<ConstructorDeclaration, number>();\n\n\tsourceFiles.forEach( ( sourceFile: SourceFile ) => {\n\t\tlogger.verbose( `  Processing classes in source file: ${sourceFile.getFilePath()}` );\n\t\tconst classes = sourceFile.getDescendantsOfKind( SyntaxKind.ClassDeclaration );\n\n\t\tclasses.forEach( ( classDeclaration: ClassDeclaration ) => {\n\t\t\tconst constructorFns = classDeclaration.getConstructors() || [];\n\t\t\tconst constructorFn = constructorFns.length > 0 ? constructorFns[ 0 ] : undefined;  // only grab the first since we're converting JavaScript\n\n\t\t\t// If there is no constructor function for this class, then nothing to do\n\t\t\tif( !constructorFn ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlogger.verbose( `    Looking for calls to the constructor of class: '${classDeclaration.getName()}'` );\n\n\t\t\tconst constructorFnParams = constructorFn.getParameters();\n\t\t\tconst numParams = constructorFnParams.length;\n\n\t\t\tconst referencedNodes = classDeclaration.findReferencesAsNodes();\n\n\t\t\tconst callsToConstructor = referencedNodes\n\t\t\t\t.map( ( node: Node ) => node.getFirstAncestorByKind( SyntaxKind.NewExpression ) )\n\t\t\t\t.filter( ( node ): node is NewExpression => !!node );\n\n\t\t\tlogger.debug( `    Found ${callsToConstructor.length} call(s) to the constructor` );\n\n\t\t\tconst minNumberOfCallArgs = callsToConstructor\n\t\t\t\t.reduce( ( minCallArgs: number, call: NewExpression ) => {\n\t\t\t\t\treturn Math.min( minCallArgs, call.getArguments().length );\n\t\t\t\t}, numParams );\n\n\t\t\tif( callsToConstructor.length > 0 ) {\n\t\t\t\tlogger.debug( `    Constructor currently expects ${numParams} params. Call(s) to the constructor supply a minimum of ${minNumberOfCallArgs} args.` );\n\t\t\t}\n\n\t\t\tconstructorMinArgsMap.set( constructorFn, minNumberOfCallArgs );\n\t\t} );\n\t} );\n\n\treturn constructorMinArgsMap;\n}\n\n\n/**\n * Finds the call sites of each FunctionDeclaration or MethodDeclaration in\n * order to determine if any of its parameters should be marked as optional.\n *\n * Returns a Map keyed by FunctionDeclaration or MethodDeclaration which contains\n * the minimum number of arguments passed to that function/method.\n *\n * Actually marking the parameters as optional is done in a separate phase.\n */\nfunction parseFunctionAndMethodCalls( sourceFiles: SourceFile[] ): Map<NameableFunction, number> {\n\tlogger.verbose( 'Finding all calls to functions/methods...' );\n\tconst functionsMinArgsMap = new Map<NameableFunction, number>();\n\n\tsourceFiles.forEach( ( sourceFile: SourceFile ) => {\n\t\tlogger.verbose( `  Processing functions/methods in source file: ${sourceFile.getFilePath()}` );\n\t\tconst fns = getFunctionsAndMethods( sourceFile );\n\n\t\tfns.forEach( ( fn: NameableFunction ) => {\n\t\t\tlogger.verbose( `    Looking for calls to the function: '${fn.getName()}'` );\n\t\t\tconst fnParams = fn.getParameters();\n\t\t\tconst numParams = fnParams.length;\n\n\t\t\tconst referencedNodes = fn.findReferencesAsNodes();\n\n\t\t\tconst callsToFunction = referencedNodes\n\t\t\t\t.map( ( node: Node ) => node.getFirstAncestorByKind( SyntaxKind.CallExpression ) )\n\t\t\t\t.filter( ( node ): node is CallExpression => !!node );\n\n\t\t\tlogger.debug( `    Found ${callsToFunction.length} call(s) to the function '${fn.getName()}'` );\n\n\t\t\tconst minNumberOfCallArgs = callsToFunction\n\t\t\t\t.reduce( ( minCallArgs: number, call: CallExpression ) => {\n\t\t\t\t\treturn Math.min( minCallArgs, call.getArguments().length );\n\t\t\t\t}, numParams );\n\n\t\t\tif( callsToFunction.length > 0 ) {\n\t\t\t\tlogger.debug( `    Function currently expects ${numParams} params. Call(s) to the function/method supply a minimum of ${minNumberOfCallArgs} args.` );\n\t\t\t}\n\n\t\t\tfunctionsMinArgsMap.set( fn, minNumberOfCallArgs );\n\t\t} );\n\t} );\n\n\treturn functionsMinArgsMap;\n}\n\n\n/**\n * Retrieves all FunctionDeclarations and MethodDeclarations from the given\n * source file.\n */\nfunction getFunctionsAndMethods(\n\tsourceFile: SourceFile\n): NameableFunction[] {\n\treturn ( [] as NameableFunction[] ).concat(\n\t\tsourceFile.getDescendantsOfKind( SyntaxKind.FunctionDeclaration ),\n\t\tsourceFile.getDescendantsOfKind( SyntaxKind.MethodDeclaration )\n\t);\n}\n\n\n\n/**\n * Marks parameters of class constructors / methods / functions as optional\n * based on the minimum number of arguments passed in at its call sites.\n *\n * Ex:\n *\n *     class SomeClass {\n *         constructor( arg1, arg2 ) {}\n *     }\n *     new SomeClass( 1 );  // no arg2\n *\n *     function myFn( arg1, arg2 ) {}\n *     myFn();  // no args\n *\n *\n * Output class and function:\n *\n *     class SomeClass {\n *         constructor( arg1, arg2? ) {}  // <-- arg2 marked as optional\n *     }\n *\n *     function myFn( arg1?, arg2? ) {}   // <-- arg1 and arg2 marked as optional\n */\nfunction addOptionals( minArgsMap: Map<FunctionTransformTarget, number> ) {\n\tconst fns = minArgsMap.keys();\n\n\tfor( const fn of fns ) {\n\t\tconst fnParams = fn.getParameters();\n\n\t\tconst numParams = fnParams.length;\n\t\tconst minNumberOfCallArgs = minArgsMap.get( fn )!;\n\n\t\t// Mark all parameters greater than the minNumberOfCallArgs as\n\t\t// optional (if it's not a rest parameter or already has a default value)\n\t\tfor( let i = minNumberOfCallArgs; i < numParams; i++ ) {\n\t\t\tconst param = fnParams[ i ];\n\n\t\t\tif( !param.isRestParameter() && !param.hasInitializer() ) {\n\t\t\t\tparam.setHasQuestionToken( true );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/converter/convert.ts",
    "content": "import { Project, SyntaxKind } from \"ts-morph\";\nimport { addClassPropertyDeclarations } from \"./add-class-property-declarations/add-class-property-declarations\";\nimport { addOptionalsToFunctionParams } from \"./add-optionals-to-function-params\";\nimport { filterOutNodeModules } from \"./filter-out-node-modules\";\nimport logger from \"../logger/logger\";\n\n/**\n * Converts the source .js code to .ts\n */\nexport function convert( tsAstProject: Project ): Project {\n\tif( tsAstProject.getSourceFiles().length === 0 ) {\n\t\tlogger.info( 'Found no source files to process. Exiting.' );\n\t\treturn tsAstProject;\n\t}\n\n\t// Print input files\n\tlogger.info( 'Processing the following source files:' );\n\tprintSourceFilesList( tsAstProject, '  ' );\n\n\tlogger.info( `\n\t\tConverting source files... This may take anywhere from a few minutes to \n\t\ttens of minutes or longer depending on how many files are being \n\t\tconverted.\n\t`.replace( /\\t*/gm, '' ) );\n\n\t// Fill in PropertyDeclarations for properties used by ES6 classes\n\tlogger.info( 'Adding property declarations to JS Classes...' );\n\ttsAstProject = addClassPropertyDeclarations( tsAstProject );\n\n\t// Rename .js files to .ts files\n\tlogger.info( 'Renaming .js files to .ts' );\n\ttsAstProject.getSourceFiles().forEach( sourceFile => {\n\t\tconst ext = sourceFile.getExtension();\n\n\t\tif( ext === '.js' || ext === '.jsx' ) {\n\t\t\tconst dir = sourceFile.getDirectoryPath();\n\t\t\tconst basename = sourceFile.getBaseNameWithoutExtension();\n\n\t\t\t// in case there's a '.js' file which has JSX in it\n\t\t\tconst fileHasJsx = sourceFile.getFirstDescendantByKind( SyntaxKind.JsxElement )\n\t\t\t\t|| sourceFile.getFirstDescendantByKind( SyntaxKind.JsxSelfClosingElement );\n\t\t\tconst extension = ( fileHasJsx || ext === '.jsx' ) ? 'tsx' : 'ts';\n\t\t\tconst outputFilePath = `${dir}/${basename}.${extension}`;\n\n\t\t\tlogger.debug( `  Renaming ${sourceFile.getFilePath()} to ${outputFilePath}` );\n\t\t\tsourceFile.move( outputFilePath );\n\t\t}\n\t} );\n\n\t// Filter out any node_modules files that accidentally got included by an import.\n\t// We don't want to modify these when we save the project\n\ttsAstProject = filterOutNodeModules( tsAstProject );\n\n\t// Make function parameters optional for calls that supply fewer arguments\n\t// than there are function parameters.\n\t// NOTE: Must happen after .js -> .ts rename for the TypeScript Language\n\t// Service to work.\n\tlogger.info( 'Making parameters optional for calls that supply fewer args than function parameters...' );\n\ttsAstProject = addOptionalsToFunctionParams( tsAstProject );\n\n\t// Filter out any node_modules files as we don't want to modify these when\n\t// we save the project. Also, some .d.ts files get included for some reason\n\t// like tslib.d.ts, so we don't want to output that as well.\n\ttsAstProject = filterOutNodeModules( tsAstProject );\n\n\t// Print output files\n\tlogger.info( 'Outputting .ts files:' );\n\tprintSourceFilesList( tsAstProject, '  ' );\n\n\t// Even though the `tsAstProject` has been mutated (it is not an immutable\n\t// data structure), return it anyway to avoid the confusion of an output\n\t// parameter.\n\treturn tsAstProject;\n}\n\n\n/**\n * Private helper to print out the source files list in the given `astProject`\n * to the console.\n */\nfunction printSourceFilesList( astProject: Project, indent = '' ) {\n\tastProject.getSourceFiles().forEach( sf => {\n\t\tlogger.info( `${indent}${sf.getFilePath()}` );\n\t} );\n}"
  },
  {
    "path": "src/converter/filter-out-node-modules.ts",
    "content": "import { Project } from \"ts-morph\";\n\n/**\n * Given a Project, removes all files that are under the node_modules folder.\n *\n * It seems the language service can pull in some .d.ts files from node_modules\n * that we don't want to be output after we save.\n */\nexport function filterOutNodeModules( tsAstProject: Project ): Project {\n\ttsAstProject.getSourceFiles().forEach( sourceFile => {\n\t\tif( sourceFile.getFilePath().includes( 'node_modules' ) ) {\n\t\t\ttsAstProject.removeSourceFile( sourceFile );\n\t\t}\n\t} );\n\n\treturn tsAstProject;\n}"
  },
  {
    "path": "src/create-ts-morph-project.ts",
    "content": "import { Project, IndentationText } from \"ts-morph\";\nimport fastGlob from 'fast-glob';\n\n/**\n * Creates a ts-morph Project by including the source files under the given\n * `directory`.\n *\n * @param directory The absolute path to the directory of .js files to\n *   include.\n * @param options\n * @param options.indentationText The text used to indent new class property\n *   declarations.\n * @param options.excludePatterns Glob patterns to exclude files.\n */\nexport function createTsMorphProject( directory: string, options: {\n\tindentationText?: IndentationText,\n\tincludePatterns?: string[],\n\texcludePatterns?: string[]\n} = {} ) {\n\tconst tsMorphProject = new Project( {\n\t\tmanipulationSettings: {\n\t\t\tindentationText: options.indentationText || IndentationText.Tab\n\t\t}\n\t} );\n\n\t// Read files using fast-glob. fast-glob does a much better job over node-glob\n\t// at ignoring directories like node_modules without reading all of the files \n\t// in them first\n\tlet files = fastGlob.sync( options.includePatterns || `**/*.+(js|ts|jsx|tsx)`, {\n\t\tcwd: directory,\n\t\tabsolute: true,\n\t\tfollowSymbolicLinks: true,\n\n\t\t// filter out any path which includes node_modules. We don't want to\n\t\t// attempt to parse those as they may be ES5, and we also don't accidentally\n\t\t// want to write out into the node_modules folder\n\t\tignore: ['**/node_modules/**'].concat(options.excludePatterns || [])\n\t} );\n\n\tfiles.forEach( ( filePath: string ) => {\n\t\ttsMorphProject.addSourceFileAtPath( filePath )\n\t} );\n\n\treturn tsMorphProject;\n}\n\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from './js-to-ts-converter';\nexport * from './logger/log-level';"
  },
  {
    "path": "src/js-to-ts-converter.ts",
    "content": "import * as path from 'path';\nimport { createTsMorphProject } from \"./create-ts-morph-project\";\nimport { convert } from \"./converter/convert\";\nimport { Project, IndentationText } from \"ts-morph\";\nimport { LogLevel } from \"./logger\";\nimport logger from \"./logger/logger\";\n\nexport interface JsToTsConverterOptions {\n\tindentationText?: IndentationText,\n\tlogLevel?: LogLevel,\n\tincludePatterns?: string[],\n\texcludePatterns?: string[]\n}\n\n/**\n * Asynchronously converts the JavaScript files under the given `sourceFilesPath`\n * to TypeScript files.\n *\n * @param sourceFilesPath The path to the source files to convert\n * @param [options]\n * @param [options.indentationText] The text used to indent new class property\n *   declarations.\n * @param [options.logLevel] The level of logging to show on the console.\n *   One of: 'debug', 'verbose', 'info', 'warn', 'error'\n * @param [options.includePatterns] Glob patterns to include files.\n * @param [options.excludePatterns] Glob patterns to exclude files.\n */\nexport async function convertJsToTs(\n\tsourceFilesPath: string,\n\toptions: JsToTsConverterOptions = {}\n): Promise<void> {\n\tconst convertedTsAstProject = doConvert( sourceFilesPath, options );\n\n\t// Save output files\n\treturn convertedTsAstProject.save();\n}\n\n/**\n * Synchronously converts the JavaScript files under the given `sourceFilesPath`\n * to TypeScript files.\n *\n * @param sourceFilesPath The path to the source files to convert\n * @param [options]\n * @param [options.indentationText] The text used to indent new class property\n *   declarations.\n * @param [options.logLevel] The level of logging to show on the console.\n *   One of: 'debug', 'verbose', 'info', 'warn', 'error'\n * @param [options.includePatterns] Glob patterns to include files.\n * @param [options.excludePatterns] Glob patterns to exclude files.\n */\nexport function convertJsToTsSync(\n\tsourceFilesPath: string,\n\toptions: JsToTsConverterOptions = {}\n) {\n\tconst convertedTsAstProject = doConvert( sourceFilesPath, options );\n\n\t// Save output files\n\tconvertedTsAstProject.saveSync();\n}\n\n\n/**\n * Performs the actual conversion given a `sourceFilesPath`, and returning a\n * `ts-morph` Project with the converted source files.\n *\n * @param sourceFilesPath The path to the source files to convert\n * @param [options]\n * @param [options.indentationText] The text used to indent new class property\n *   declarations.\n * @param [options.logLevel] The level of logging to show on the console.\n *   One of: 'debug', 'verbose', 'info', 'warn', 'error'\n * @param [options.includePatterns] Glob patterns to include files.\n * @param [options.excludePatterns] Glob patterns to exclude files.\n */\nfunction doConvert(\n\tsourceFilesPath: string,\n\toptions: JsToTsConverterOptions = {}\n): Project {\n\tlogger.setLogLevel( options.logLevel || 'verbose' );\n\n\tconst absolutePath = path.resolve( sourceFilesPath );\n\n\tconst tsAstProject = createTsMorphProject( absolutePath, options );\n\treturn convert( tsAstProject );\n}"
  },
  {
    "path": "src/logger/index.ts",
    "content": "export * from './logger';\nexport * from './log-level';\n\nimport logger from './logger';\nexport default logger;"
  },
  {
    "path": "src/logger/log-level.ts",
    "content": "export type LogLevel = 'debug' | 'verbose' | 'info' | 'warn' | 'error';\nexport const logLevels = [ 'debug', 'verbose', 'info', 'warn', 'error' ];\n"
  },
  {
    "path": "src/logger/logger.ts",
    "content": "import * as winston from 'winston';\nimport { LogLevel } from \"./log-level\";\n\nconst winstonLogger = winston.createLogger( {\n\tlevel: 'verbose',  // may be changed by Logger.setLogLevel()\n\ttransports: [\n\t\tnew winston.transports.Console( {\n\t\t\tformat: winston.format.combine(\n\t\t\t\twinston.format.colorize(),\n\t\t\t\twinston.format.align(),\n\t\t\t\twinston.format.printf(info => `${info.level}: ${info.message}`)\n\t\t\t)\n\t\t} )\n\t]\n} );\n\n\n/**\n * Abstraction layer for the Winston logger. The methods are in order from\n * highest level of logging to lowest.\n */\nclass Logger {\n\n\tsetLogLevel( logLevel: LogLevel ) {\n\t\twinstonLogger.level = logLevel;\n\t}\n\n\tdebug( message: string ) {\n\t\twinstonLogger.log( 'debug', message );\n\t}\n\n\tverbose( message: string ) {\n\t\twinstonLogger.log( 'verbose', message );\n\t}\n\n\tinfo( message: string ) {\n\t\twinstonLogger.log( 'info', message );\n\t}\n\n\tlog( message: string ) {\n\t\twinstonLogger.log( 'info', message );\n\t}\n\n\twarn( message: string ) {\n\t\twinstonLogger.log( 'warn', message );\n\t}\n\n\terror( message: string ) {\n\t\twinstonLogger.log( 'error', message );\n\t}\n\n}\n\nconst logger = new Logger();\nexport default logger;"
  },
  {
    "path": "src/util/find-import-for-identifier.ts",
    "content": "import { ImportDeclaration, ImportSpecifier, SourceFile } from \"ts-morph\";\n\n/**\n * Finds an ImportDeclaration for a given identifier (name).\n *\n * For instance, given this source file:\n *\n *     import { SomeClass1, SomeClass2 } from './somewhere';\n *     import { SomeClass3 } from './somewhere-else';\n *\n *     // ...\n *\n * And a call such as:\n *\n *     findImportForIdentifier( sourceFile, 'SomeClass3' );\n *\n * Then the second ImportDeclaration will be returned.\n */\nexport function findImportForIdentifier(\n\tsourceFile: SourceFile,\n\tidentifier: string\n): ImportDeclaration | undefined {\n\treturn sourceFile\n\t\t.getImportDeclarations()\n\t\t.find( ( importDeclaration: ImportDeclaration ) => {\n\t\t\tconst hasNamedImport = importDeclaration.getNamedImports()\n\t\t\t\t.map( ( namedImport: ImportSpecifier ) => namedImport.getName() )\n\t\t\t\t.includes( identifier );\n\n\t\t\tconst defaultImport = importDeclaration.getDefaultImport();\n\t\t\tconst hasDefaultImport = !!defaultImport && defaultImport.getText() === identifier;\n\n\t\t\treturn hasNamedImport || hasDefaultImport;\n\t\t} );\n}"
  },
  {
    "path": "src/util/is-element-access-with-obj.ts",
    "content": "import { ElementAccessExpression, Identifier, Node } from \"ts-morph\";\n\n/**\n * Determines if the given `node` is a ElementAccessExpression whose object is\n * `obj`.\n *\n * Example, in the following expression:\n *\n *     obj['a']\n *\n * This function will return true if called as:\n *\n *     isElementAccessWithObj( expr, 'obj' );\n */\nexport function isElementAccessWithObj(\n\tnode: Node,\n\tobjIdentifier: string\n): node is ElementAccessExpression {\n\tif( !Node.isElementAccessExpression( node ) ) {\n\t\treturn false;\n\t}\n\n\tconst expr = node.getExpression();\n\n\tif( objIdentifier === 'this' ) {\n\t\treturn Node.isThisExpression( expr );\n\n\t} else if( Node.isIdentifier( expr ) ) {\n\t\tconst identifier = expr as Identifier;\n\n\t\treturn identifier.getText() === objIdentifier;\n\n\t} else {\n\t\treturn false;\n\t}\n}\n\n/**\n * Function intended to be used with Array.prototype.filter() to return any\n * ElementAccessExpression that uses the object `obj`.\n *\n * For example, in this source code:\n *\n *     const obj = { a: 1, b: 2 };\n *     obj['a'] = 3;\n *\n *     const obj2 = { a: 3, b: 4 };\n *     obj2['b'] = 5;\n *\n * We can use the following to find the 'obj2' element access:\n *\n *     const propAccesses = sourceFile\n *         .getDescendantsOfKind( SyntaxKind.ElementAccessExpression );\n *\n *     const obj2PropAccesses = propAccesses\n *         .filter( elementAccessWithObjFilter( 'obj2' ) );\n */\nexport function elementAccessWithObjFilter( objIdentifier: string ): ( node: Node ) => node is ElementAccessExpression {\n\treturn ( node: Node ): node is ElementAccessExpression => {\n\t\treturn isElementAccessWithObj( node, objIdentifier );\n\t};\n}"
  },
  {
    "path": "src/util/is-property-access-with-obj.ts",
    "content": "import { Identifier, Node, PropertyAccessExpression } from \"ts-morph\";\n\n/**\n * Determines if the given `node` is a PropertyAccessExpression or\n * ElementAccessExpression whose object is `obj`.\n *\n * Example, in the following expression:\n *\n *     obj.a\n *\n * This function will return true if called as:\n *\n *     isPropertyOrElemementAccessWithObj( expr, 'obj' );\n */\nexport function isPropertyAccessWithObj(\n\tnode: Node,\n\tobjIdentifier: string\n): node is PropertyAccessExpression {\n\tif( !Node.isPropertyAccessExpression( node ) ) {\n\t\treturn false;\n\t}\n\n\tconst expr = node.getExpression();\n\n\tif( objIdentifier === 'this' ) {\n\t\treturn Node.isThisExpression( expr );\n\n\t} else if( Node.isIdentifier( expr ) ) {\n\t\tconst identifier = expr as Identifier;\n\n\t\treturn identifier.getText() === objIdentifier;\n\n\t} else {\n\t\treturn false;\n\t}\n}\n\n/**\n * Function intended to be used with Array.prototype.filter() to return any\n * PropertyAccessExpression that uses the object `obj`.\n *\n * For example, in this source code:\n *\n *     const obj = { a: 1, b: 2 };\n *     obj.a = 3;\n *\n *     const obj2 = { a: 3, b: 4 };\n *     obj2.b = 5;\n *\n * We can use the following to find the 'obj2' property access:\n *\n *     const propAccesses = sourceFile\n *         .getDescendantsOfKind( SyntaxKind.PropertyAccessExpression );\n *\n *     const obj2PropAccesses = propAccesses\n *         .filter( propAccessWithObjFilter( 'obj2' ) );\n */\nexport function propertyAccessWithObjFilter( objIdentifier: string ): ( node: Node ) => node is PropertyAccessExpression {\n\treturn ( node: Node ): node is PropertyAccessExpression => {\n\t\treturn isPropertyAccessWithObj( node, objIdentifier );\n\t};\n}"
  },
  {
    "path": "src/util/is-property-or-elemement-access-with-obj.ts",
    "content": "import { ElementAccessExpression, Node, PropertyAccessExpression } from \"ts-morph\";\nimport { isPropertyAccessWithObj } from \"./is-property-access-with-obj\";\nimport { isElementAccessWithObj } from \"./is-element-access-with-obj\";\n\n/**\n * Determines if the given `node` is a PropertyAccessExpression or\n * ElementAccessExpression whose object is `obj`.\n *\n * Example, in the following expression:\n *\n *     obj.a\n *\n * This function will return true if called as:\n *\n *     isPropertyOrElemementAccessWithObj( expr, 'obj' );\n */\nexport function isPropertyOrElemementAccessWithObj(\n\tnode: Node,\n\tobjIdentifier: string\n): node is PropertyAccessExpression | ElementAccessExpression {\n\treturn isPropertyAccessWithObj( node, objIdentifier )\n\t\t|| isElementAccessWithObj( node, objIdentifier );\n}\n\n/**\n * Function intended to be used with Array.prototype.filter() to return any\n * PropertyAccessExpression or ElementAccessExpression that uses the object\n * `obj`.\n *\n * For example, in this source code:\n *\n *     const obj = { a: 1, b: 2 };\n *     obj.a = 3;\n *     obj['b'] = 4;\n *\n *     const obj2 = { a: 3, b: 4 };\n *     obj2.a = 5;\n *     obj2['b'] = 6;\n *\n * We can use the following to find the two 'obj2' property accesses:\n *\n *     const propOrElementAccesses = sourceFile\n *         .getDescendantsOfKind( SyntaxKind.PropertyAccessExpression )\n *         .concat( sourceFile\n *             .getDescendantsOfKind( SyntaxKind.ElementAccessExpression )\n *         );\n *\n *     const obj2PropOrElemAccesses = propOrElementAccesses\n *         .filter( propertyOrElementAccessWithObjFilter( 'obj2' ) );\n */\nexport function propertyOrElementAccessWithObjFilter( objIdentifier: string ): ( node: Node ) => node is PropertyAccessExpression | ElementAccessExpression {\n\treturn ( node: Node ): node is PropertyAccessExpression | ElementAccessExpression => {\n\t\treturn isPropertyOrElemementAccessWithObj( node, objIdentifier );\n\t};\n}"
  },
  {
    "path": "src/util/is-this-referencing-var.ts",
    "content": "import { Node, SyntaxKind, VariableDeclaration } from \"ts-morph\";\n\n/**\n * Determines if the given AST Node is a VariableDeclaration of the form:\n *\n *     var self = this;\n *\n *\n * Will return false for the following, however, since this is a destructuring\n * of the `this` object's properties.\n *\n *     var { prop1, prop2 } = this;\n */\nexport function isThisReferencingVar( node: Node ): node is VariableDeclaration {\n\tif( !Node.isVariableDeclaration( node ) ) {\n\t\treturn false;\n\t}\n\n\tconst varDec = node as VariableDeclaration;\n\n\tconst initializerIsThisKeyword = !!varDec.getInitializerIfKind( SyntaxKind.ThisKeyword );\n\tconst assignedToSingleIdentifier = varDec.compilerNode.name.kind === SyntaxKind.Identifier;\n\n\treturn initializerIsThisKeyword && assignedToSingleIdentifier;\n}"
  },
  {
    "path": "src/util/is-valid-identifier.ts",
    "content": "/**\n * Helper to determine if a string of text is a valid JavaScript identifier.\n */\nexport function isValidIdentifier( text: string ) {\n\treturn /^[\\w$]+$/.test( text );\n}"
  },
  {
    "path": "src/util/parse-destructured-props.ts",
    "content": "import { ts, SyntaxKind } from \"ts-morph\";\n\n/**\n * Given a ts.ObjectBindingPattern node, returns an array of the names that\n * are bound to it.\n *\n * These names are essentially the property names pulled out of the object.\n *\n * Example:\n *\n *     var { a, b } = this;\n *\n * Returns:\n *\n *     [ 'a', 'b' ]\n */\nexport function parseDestructuredProps( node: ts.ObjectBindingPattern ): string[] {\n\tconst elements = node.elements;\n\n\treturn elements\n\t\t.filter( ( element: ts.BindingElement ) => {\n\t\t\treturn element.name.kind === SyntaxKind.Identifier;\n\t\t} )\n\t\t.map( ( element: ts.BindingElement ) => {\n\t\t\treturn ( element.name as ts.Identifier ).text;\n\t\t} );\n}"
  },
  {
    "path": "src/util/set-utils.ts",
    "content": "/**\n * Unions two or more sets to create a combined set. Does not mutate the input\n * sets.\n */\nexport function union<T>( setA: Set<T>, ...sets: Set<T>[] ) {\n\tconst union = new Set<T>( setA );\n\n\tsets.forEach( currentSet => {\n\t\tfor( const elem of currentSet ) {\n\t\t\tunion.add( elem );\n\t\t}\n\t} );\n\treturn union;\n}\n\n\n/**\n * Removes the elements of `setB` from `setA` to produce the difference. Does\n * not mutate the input sets.\n */\nexport function difference<T>( setA: Set<T>, setB: Set<T> ) {\n\tconst difference = new Set( setA );\n\tfor( const elem of setB ) {\n\t\tdifference.delete( elem );\n\t}\n\treturn difference;\n}"
  },
  {
    "path": "test/convert.spec.ts",
    "content": "import { expect } from 'chai';\nimport { createTsMorphProject } from \"../src/create-ts-morph-project\";\nimport { convert } from \"../src/converter/convert\";\nimport { SourceFile } from \"ts-morph\";\nimport * as fs from \"fs\";\nimport logger from \"../src/logger/logger\";\nimport { JsToTsConverterOptions } from \"../src\";\n\n// Minimal logging for tests\nlogger.setLogLevel( 'error' );\n\ndescribe( 'convert()', () => {\n\n\tit( `should convert JS classes to TS-compilable classes by filling in field\n\t     (property) declarations for properties consumed in the original JS \n\t     classes`,\n\t() => {\n\t\trunTest( `${__dirname}/fixture/superclass-subclass` );\n\t} );\n\n\n\tit( `should ignore expressions (i.e. non-identifiers) in the 'extends' \n\t     clause of a class (at least for the moment, this would be too much\n\t     to parse and figure out - may support in the future)`,\n\t() => {\n\t\trunTest( `${__dirname}/fixture/expression-extends` );\n\t} );\n\n\n\tit( `should not fill in property declarations for properties that are already\n\t     declared (such as if the utility is run against a typescript codebase),\n\t     but should fill in any missing properties that are not declared`,\n\t() => {\n\t\trunTest( `${__dirname}/fixture/typescript-class` );\n\t} );\n\n\n\tit( `should handle 'var this = that' by adding 'that.xyz' as a class\n\t     property declaration`,\n\t() => {\n\t\trunTest( `${__dirname}/fixture/function-expressions-and-declarations` );\n\t} );\n\n\n\tit( `should make function parameters optional when call sites are found to\n\t     supply fewer arguments than there are parameters`,\n\t() => {\n\t\trunTest( `${__dirname}/fixture/function-calls-with-fewer-args-than-params` );\n\t} );\n\n\n\tit( `should not require node_modules to be installed in order to convert a\n\t     codebase`,\n\t() => {\n\t\trunTest( `${__dirname}/fixture/superclass-subclass-node-modules-not-installed` );\n\t} );\n\n\n\tit( `should properly handle includePatterns and excludePatterns options`, () => {\n\t\trunTest( `${__dirname}/fixture/include-exclude-patterns`, {\n\t\t\tincludePatterns: [ '**/included/**' ],\n\t\t\texcludePatterns: [ '**/included/excluded/**' ]\n\t\t} );\n\t} );\n\n\tit( `should properly convert a React .jsx file to .tsx`, () => {\n\t\trunTest( `${__dirname}/fixture/react-class-jsx` );\n\t} );\n\n\tit( `should properly convert a React .js file which has JSX within it\n\t\t to .tsx`, \n\t() => {\n\t\trunTest( `${__dirname}/fixture/react-class-js` );\n\t} );\n\n\tit( `should properly convert a React .js file which has only a self-closing JSX\n\t\t tag within it to .tsx (https://github.com/gregjacobs/js-to-ts-converter/issues/15),\n\t\t and also not error with a self-closing JSX element (https://github.com/gregjacobs/js-to-ts-converter/issues/4)`,\n\t() => {\n\t\trunTest( `${__dirname}/fixture/react-jsx-self-closing-element` );\n\t} );\n\n\tit( `should not do anything with a reference to this.constructor (https://github.com/gregjacobs/js-to-ts-converter/issues/9)`,\n\t() => {\n\t\trunTest( `${__dirname}/fixture/class-with-this-constructor-reference` );\n\t} );\n\n} );\n\n\n/**\n * Runs a test of the conversion utility by passing it a directory that has\n * two subdirectories:\n *\n * - input\n * - expected\n *\n * The `input` directory will be converted, and then compared to the\n * `expected` directory.\n *\n * @param absolutePath Absolute path to the directory which has\n *   `input` and `expected` subdirectories.\n * @param [inputFilesOptions] The options to configure the converter.\n */\nfunction runTest(\n\tabsolutePath: string,\n\tinputFilesOptions?: JsToTsConverterOptions\n) {\n\tif( !fs.lstatSync( absolutePath ).isDirectory() ) {\n\t\tthrow new Error( 'The absolute path: ' + absolutePath + ' is not a directory' );\n\t}\n\tif( !fs.lstatSync( absolutePath + '/input' ).isDirectory() ) {\n\t\tthrow new Error( 'The absolute path: ' + absolutePath + '/input is not a directory' );\n\t}\n\tif( !fs.lstatSync( absolutePath + '/expected' ).isDirectory() ) {\n\t\tthrow new Error( 'The absolute path: ' + absolutePath + '/expected is not a directory' );\n\t}\n\n\tconst inputFilesProject = createTsMorphProject( absolutePath + '/input', inputFilesOptions );\n\tconst expectedFilesProject = createTsMorphProject( absolutePath + '/expected' );\n\n\tif( inputFilesProject.getSourceFiles().length === 0 ) {\n\t\tthrow new Error( `No source files were found in the input directory: ${absolutePath}/input` );\n\t}\n\n\tconst convertedInputProject = convert( inputFilesProject );\n\n\tconst convertedSourceFiles = convertedInputProject.getSourceFiles();\n\tconst expectedSourceFiles = expectedFilesProject.getSourceFiles();\n\tconst convertedSourceFilePaths = convertedInputProject.getSourceFiles().map( sf => sf.getFilePath() );\n\tconst expectedSourceFilePaths = expectedFilesProject.getSourceFiles().map( sf => sf.getFilePath() );\n\n\t// First, make sure that there are the same number of files in the converted\n\t// and expected projects\n\tif( convertedSourceFiles.length !== expectedSourceFiles.length ) {\n\t\tthrow new Error( `\n\t\t\tThe number of converted source files (${convertedSourceFiles.length})\n\t\t\tdoes not match the number of expected source files (${expectedSourceFiles.length}).\n\t\t\t\n\t\t\tConverted source files:\n\t\t\t  ${convertedSourceFilePaths.join( '\\n  ' )}\n\t\t\t  \n\t\t\tExpected source files:\n\t\t\t  ${expectedSourceFilePaths.join( '\\n  ' )}\n\t\t`.replace( /^\\t*/gm, '' ) )\n\t}\n\n\t// Now check each converted source file against the expected output file\n\tconvertedSourceFiles.forEach( ( convertedSourceFile: SourceFile ) => {\n\t\tconst expectedSourceFilePath = convertedSourceFile.getFilePath().replace( /([\\\\\\/])input[\\\\\\/]/, '$1expected$1' );\n\t\tconst expectedSourceFile = expectedFilesProject.getSourceFile( expectedSourceFilePath );\n\n\t\tif( !expectedSourceFile ) {\n\t\t\tthrow new Error( `\n\t\t\t\tThe converted source file (below) does not have a matching 'expected' file: \n\t\t\t\t  '${convertedSourceFile.getFilePath()}'\n\t\t\t\t  \n\t\t\t\tTried to find matching expected file: \n\t\t\t\t  '${expectedSourceFilePath}'\n\t\t\t`.replace( /^\\t*/gm, '' ) );\n\t\t}\n\n\t\texpect( convertedSourceFile.getFullText() )\n\t\t\t.to.equal( expectedSourceFile!.getFullText() );\n\t} );\n\n}\n"
  },
  {
    "path": "test/fixture/class-with-this-constructor-reference/expected/my-class.ts",
    "content": "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",
    "content": "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",
    "content": "class ExpressionExtends extends Mixin.mix( SomeClass1, SomeClass2 ) {\n\tpublic someProp: any;\n\n\tconstructor() {\n\t\tthis.someProp = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/expression-extends/input/expression-extends.js",
    "content": "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",
    "content": "import { myExportedFunction } from \"./exported-function\";\n\nmyExportedFunction();  // no args - should mark all as optional"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-local-function-with-default-value.ts",
    "content": "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",
    "content": "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",
    "content": "import { SubClass } from \"./sub-class\";\n\nconst subClass = new SubClass();\n\nsubClass.subclassMethod( 1, 2 );  // should *not* mark any args as optional\n\nsubClass.subclassMethod2();  // should mark both arg1 and arg2 as optional\n"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/call-to-super-class-method.ts",
    "content": "import { SuperClass } from \"./super-class\";\n\nconst superclass = new SuperClass( 1 );  // marks arg2 and arg3 as optional\n\nsuperclass.somePublicMethod( 1 );  // marks arg2 as optional\n"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/constructor-with-rest-param.ts",
    "content": "class ConstructorWithRestParam {\n\n\tconstructor( ...args ) {  // should *not* be marked as optional\n\n\t}\n\n\tmethodWithRestParam( ...args ) {}  // should *not* be marked as optional\n\n}\n\n\nconst instance = new ConstructorWithRestParam();\ninstance.methodWithRestParam();"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/exported-function.ts",
    "content": "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",
    "content": "import { SuperClass } from \"./super-class\";\n\nexport class SubClass extends SuperClass {\n\n\tsubclassMethod( arg1, arg2 ) {  // these should *not* be made optional by the call in call-to-sub-class-method.js\n\t\t// call superclass method\n\t\tthis.superclassMethod();  // marks the arg as optional\n\t}\n\n\n\tsubclassMethod2( arg1?, arg2? ) {  // these should both be made optional by the call in call-to-sub-class-method.js\n\n\t}\n\n}"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/expected/super-class.ts",
    "content": "export class SuperClass {\n\n\tconstructor( arg1, arg2?, arg3? ) {  // arg2 and arg3 will be marked optional by call-to-superclass-method.js\n\n\t}\n\n\n\tsuperclassMethod( arg? ) {  // arg will be marked optional by sub-class.js\n\n\t}\n\n\n\tsomePublicMethod( arg1, arg2? ) {  // arg2 will be marked optional by call-to-class-method.js\n\n\t}\n\n}"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/call-to-exported-function.js",
    "content": "import { myExportedFunction } from \"./exported-function\";\n\nmyExportedFunction();  // no args - should mark all as optional"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/call-to-local-function-with-default-value.js",
    "content": "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",
    "content": "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",
    "content": "import { SubClass } from \"./sub-class\";\n\nconst subClass = new SubClass();\n\nsubClass.subclassMethod( 1, 2 );  // should *not* mark any args as optional\n\nsubClass.subclassMethod2();  // should mark both arg1 and arg2 as optional\n"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/call-to-super-class-method.js",
    "content": "import { SuperClass } from \"./super-class\";\n\nconst superclass = new SuperClass( 1 );  // marks arg2 and arg3 as optional\n\nsuperclass.somePublicMethod( 1 );  // marks arg2 as optional\n"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/constructor-with-rest-param.js",
    "content": "class ConstructorWithRestParam {\n\n\tconstructor( ...args ) {  // should *not* be marked as optional\n\n\t}\n\n\tmethodWithRestParam( ...args ) {}  // should *not* be marked as optional\n\n}\n\n\nconst instance = new ConstructorWithRestParam();\ninstance.methodWithRestParam();"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/exported-function.js",
    "content": "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",
    "content": "import { SuperClass } from \"./super-class\";\n\nexport class SubClass extends SuperClass {\n\n\tsubclassMethod( arg1, arg2 ) {  // these should *not* be made optional by the call in call-to-sub-class-method.js\n\t\t// call superclass method\n\t\tthis.superclassMethod();  // marks the arg as optional\n\t}\n\n\n\tsubclassMethod2( arg1, arg2 ) {  // these should both be made optional by the call in call-to-sub-class-method.js\n\n\t}\n\n}"
  },
  {
    "path": "test/fixture/function-calls-with-fewer-args-than-params/input/super-class.js",
    "content": "export class SuperClass {\n\n\tconstructor( arg1, arg2, arg3 ) {  // arg2 and arg3 will be marked optional by call-to-superclass-method.js\n\n\t}\n\n\n\tsuperclassMethod( arg ) {  // arg will be marked optional by sub-class.js\n\n\t}\n\n\n\tsomePublicMethod( arg1, arg2 ) {  // arg2 will be marked optional by call-to-class-method.js\n\n\t}\n\n}"
  },
  {
    "path": "test/fixture/function-expressions-and-declarations/expected/class-with-function-expressions.ts",
    "content": "class ClassWithFunctionExpressions {\n\tpublic destructured1: any;\n\tpublic destructured2: any;\n\tpublic prop1: any;\n\tpublic prop2: any;\n\tpublic innerAccessedProp: any;\n\tpublic blah: any;\n\n\tmyMethod() {\n\t\tvar that = this;\n\n\t\tvar myFn1 = function() {\n\t\t\tthat.prop1 = 1;\n\t\t}\n\n\t\tvar myFn2 = function(a, b) {\n\t\t\tthat.prop2 = 1;\n\t\t\tthat['prop3'] = 2;\n\t\t}\n\t}\n\n\tmyMethod2() {\n\t\tvar self = this,\n\t\t    somethingElse = 1;\n\n\t\tvar myFn1 = function() {\n\t\t\tself.prop1 = 1;\n\n\t\t\tvar myNestedFn = function() {\n\t\t\t\tself.innerAccessedProp = 2;\n\t\t\t}\n\t\t}\n\t}\n\n\tmyMethod3() {\n\t\tvar somethingElse = 1,\n\t\t    me = this;\n\n\t\tvar myFn1 = function() {\n\t\t\tme.prop1 = 1;\n\t\t}\n\t}\n\n\tdestructuredThis() {\n\t\t// should simply not throw an error on this construct, while populating\n\t\t// these variables as PropertyDeclarations\n\t\tconst { destructured1, destructured2 } = this;\n\t}\n\n\tcomplexMethodWhichCausesErrorInTsSimpleAstTransforms() {\n\t\tconst that = this;\n\n\t\tthat.blah.blah2.blah3 = 42;\n\t\tthat.blah.blah2.blah3.blah4 = 43;\n\n\t\t// below is potentially another test to check, but above seems to\n\t\t// display the previous bug\n\t\t//\n\t\t// if( this.asdf ) {\n\t\t// \t_.someFn( that.asdf.asdf2, () => {\n\t\t// \t\t_.someOtherFn( that.blah.blah2.blah3, () => {\n\t\t// \t\t} );\n\t\t// } );\n\t\t// }\n\n\t\t// if( !this.something ) {\n\t\t// \tthis.something = this.someOtherThing.fn( () => {\n\t\t// \t\tconst abc = [];\n\t\t//\n\t\t// \t\t_.forEach(that.model.something.else, (a) => {\n\t\t// \t\t\t// if( asdf ) {\n\t\t// \t\t\t// \tthat.model.something = 1;\n\t\t// \t\t\t// } else {\n\t\t// \t\t\t// \tthat.model.somethingElse = 2;\n\t\t// \t\t\t// }\n\t\t// \t\t} );\n\t\t//\n\t\t// \t\tthat.model.something = 42;\n\t\t// \t\t_.set( that.model.something, 'abc', 'def' );\n\t\t// \t\tthat.somethingElse = 11;\n\t\t// \t} );\n\t\t// }\n\t}\n\n}"
  },
  {
    "path": "test/fixture/function-expressions-and-declarations/input/class-with-function-expressions.js",
    "content": "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}\n\n\t\tvar myFn2 = function(a, b) {\n\t\t\tthat.prop2 = 1;\n\t\t\tthat['prop3'] = 2;\n\t\t}\n\t}\n\n\tmyMethod2() {\n\t\tvar self = this,\n\t\t    somethingElse = 1;\n\n\t\tvar myFn1 = function() {\n\t\t\tself.prop1 = 1;\n\n\t\t\tvar myNestedFn = function() {\n\t\t\t\tself.innerAccessedProp = 2;\n\t\t\t}\n\t\t}\n\t}\n\n\tmyMethod3() {\n\t\tvar somethingElse = 1,\n\t\t    me = this;\n\n\t\tvar myFn1 = function() {\n\t\t\tme.prop1 = 1;\n\t\t}\n\t}\n\n\tdestructuredThis() {\n\t\t// should simply not throw an error on this construct, while populating\n\t\t// these variables as PropertyDeclarations\n\t\tconst { destructured1, destructured2 } = this;\n\t}\n\n\tcomplexMethodWhichCausesErrorInTsSimpleAstTransforms() {\n\t\tconst that = this;\n\n\t\tthat.blah.blah2.blah3 = 42;\n\t\tthat.blah.blah2.blah3.blah4 = 43;\n\n\t\t// below is potentially another test to check, but above seems to\n\t\t// display the previous bug\n\t\t//\n\t\t// if( this.asdf ) {\n\t\t// \t_.someFn( that.asdf.asdf2, () => {\n\t\t// \t\t_.someOtherFn( that.blah.blah2.blah3, () => {\n\t\t// \t\t} );\n\t\t// } );\n\t\t// }\n\n\t\t// if( !this.something ) {\n\t\t// \tthis.something = this.someOtherThing.fn( () => {\n\t\t// \t\tconst abc = [];\n\t\t//\n\t\t// \t\t_.forEach(that.model.something.else, (a) => {\n\t\t// \t\t\t// if( asdf ) {\n\t\t// \t\t\t// \tthat.model.something = 1;\n\t\t// \t\t\t// } else {\n\t\t// \t\t\t// \tthat.model.somethingElse = 2;\n\t\t// \t\t\t// }\n\t\t// \t\t} );\n\t\t//\n\t\t// \t\tthat.model.something = 42;\n\t\t// \t\t_.set( that.model.something, 'abc', 'def' );\n\t\t// \t\tthat.somethingElse = 11;\n\t\t// \t} );\n\t\t// }\n\t}\n\n}"
  },
  {
    "path": "test/fixture/include-exclude-patterns/expected/included/included-file.ts",
    "content": "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",
    "content": "class ExcludedFile {\n\tconstructor() {\n\t\tthis.someProp = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/include-exclude-patterns/input/included/included-file.js",
    "content": "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",
    "content": "class OtherFileThatShouldNotBeIncluded {}"
  },
  {
    "path": "test/fixture/react-class-js/expected/react-class.tsx",
    "content": "import * as React from \"react\";\nimport PropTypes from \"prop-types\";\nimport {\n  TableHead, TableRow, TableCell,\n  TableSortLabel, Checkbox\n} from \"@material-ui/core\";\nimport { Draggable } from \"react-beautiful-dnd\";\n\nclass TableHeader extends React.Component {\n\tpublic props: any;\n\n  renderHeader() {\n    const mapArr = this.props.columns.filter(columnDef => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1))\n      .map((columnDef, index) => (\n        <TableCell\n          key={columnDef.tableData.id}\n          align={[\"numeric\"].indexOf(columnDef.type) !== -1 ? \"right\" : \"left\"}\n\n          style={{ ...this.props.headerStyle, ...columnDef.headerStyle }}\n        >\n          <div className=\"displayFlex\">\n            <div>\n              {(columnDef.sort !== false && columnDef.sorting !== false && this.props.sorting)\n                ? <TableSortLabel\n                  active={this.props.orderBy === columnDef.tableData.id}\n                  direction={this.props.orderDirection || \"asc\"}\n                  onClick={() => {\n                    const orderDirection = columnDef.tableData.id !== this.props.orderBy ? \"asc\" : this.props.orderDirection === \"asc\" ? \"desc\" : \"asc\";\n                    this.props.onOrderChange(columnDef.tableData.id, orderDirection);\n                  }}\n                >\n                  {(this.props.grouping && columnDef.field)\n                    ? <Draggable\n                      key={columnDef.tableData.id}\n                      draggableId={columnDef.tableData.id.toString()}\n                      index={index}>\n                      {(provided) => (\n                        <div\n                          ref={provided.innerRef}\n                          {...provided.draggableProps}\n                          {...provided.dragHandleProps}                          \n                        >\n                          {columnDef.title}\n                        </div>\n                      )}\n                    </Draggable>\n                    : columnDef.title\n                  }\n                </TableSortLabel>\n                : columnDef.title\n              }\n            </div>\n            {columnDef.filter && \n                    <div>\n                      {columnDef.filter}\n                    </div>\n\n            }\n          </div>\n        </TableCell>\n      ));\n    return mapArr;\n  }\n\n  renderActionsHeader() {\n    const localization = { ...TableHeader.defaultProps.localization, ...this.props.localization };\n    return (\n      <TableCell\n        key=\"key-actions-column\"\n        style={this.props.headerStyle}\n      >\n        <TableSortLabel>{localization.actions}</TableSortLabel>\n      </TableCell>\n    );\n  }\n  renderSelectionHeader() {\n    return (\n      <TableCell\n        padding=\"none\"\n        key=\"key-selection-column\"\n        style={this.props.headerStyle}\n      >\n        <Checkbox\n          indeterminate={this.props.selectedCount > 0 && this.props.selectedCount < this.props.dataCount}\n          checked={this.props.selectedCount === this.props.dataCount}\n          onChange={(event, checked) => this.props.onAllSelected && this.props.onAllSelected(checked)}\n        />\n      </TableCell>\n    );\n  }\n  render() {\n    const headers = this.renderHeader();\n    if (this.props.hasSelection && this.props.dataCount) {\n      headers.splice(0, 0, this.renderSelectionHeader());\n    }\n\n    if (this.props.showActionsColumn) {\n      if (this.props.actionsHeaderIndex >= 0) {\n        let endPos = 0;\n        if (this.props.hasSelection) {\n          endPos = 1;\n        }\n        headers.splice(this.props.actionsHeaderIndex + endPos, 0, this.renderActionsHeader());\n      } else if (this.props.actionsHeaderIndex === -1) {\n        headers.push(this.renderActionsHeader());\n      }\n    }\n\n    if (this.props.hasDetailPanel) {\n      headers.splice(0, 0,\n        <TableCell\n          padding=\"none\"\n          key=\"key-detail-panel-column\"\n          style={this.props.headerStyle}\n        />\n      );\n    }\n\n    this.props.columns\n      .filter(columnDef => columnDef.tableData.groupOrder > -1)\n      .forEach(columnDef => {\n        headers.splice(0, 0, <TableCell padding=\"checkbox\" key={\"key-group-header\" + columnDef.tableData.id} />);\n      });\n\n    return (\n      <TableHead>\n        <TableRow>\n          {headers}\n        </TableRow>\n      </TableHead>\n    );\n  }\n}\n\nTableHeader.defaultProps = {\n  dataCount: 0,\n  hasSelection: false,\n  headerStyle: {},\n  selectedCount: 0,\n  sorting: true,\n  localization: {\n    actions: \"Actions\" \n  },\n  orderBy: undefined,\n  orderDirection: \"asc\",\n  actionsHeaderIndex: 0\n};\n\nTableHeader.propTypes = {\n  columns: PropTypes.array.isRequired,\n  dataCount: PropTypes.number,\n  hasDetailPanel: PropTypes.bool.isRequired,\n  hasSelection: PropTypes.bool,\n  headerStyle: PropTypes.object,\n  localization: PropTypes.object,\n  selectedCount: PropTypes.number,\n  sorting: PropTypes.bool,\n  onAllSelected: PropTypes.func,\n  onOrderChange: PropTypes.func,\n  orderBy: PropTypes.number,\n  orderDirection: PropTypes.string,\n  actionsHeaderIndex: PropTypes.number,\n  showActionsColumn: PropTypes.bool,\n};\n\nexport default TableHeader;"
  },
  {
    "path": "test/fixture/react-class-js/input/react-class.js",
    "content": "import * as React from \"react\";\nimport PropTypes from \"prop-types\";\nimport {\n  TableHead, TableRow, TableCell,\n  TableSortLabel, Checkbox\n} from \"@material-ui/core\";\nimport { Draggable } from \"react-beautiful-dnd\";\n\nclass TableHeader extends React.Component {\n    \n  renderHeader() {\n    const mapArr = this.props.columns.filter(columnDef => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1))\n      .map((columnDef, index) => (\n        <TableCell\n          key={columnDef.tableData.id}\n          align={[\"numeric\"].indexOf(columnDef.type) !== -1 ? \"right\" : \"left\"}\n\n          style={{ ...this.props.headerStyle, ...columnDef.headerStyle }}\n        >\n          <div className=\"displayFlex\">\n            <div>\n              {(columnDef.sort !== false && columnDef.sorting !== false && this.props.sorting)\n                ? <TableSortLabel\n                  active={this.props.orderBy === columnDef.tableData.id}\n                  direction={this.props.orderDirection || \"asc\"}\n                  onClick={() => {\n                    const orderDirection = columnDef.tableData.id !== this.props.orderBy ? \"asc\" : this.props.orderDirection === \"asc\" ? \"desc\" : \"asc\";\n                    this.props.onOrderChange(columnDef.tableData.id, orderDirection);\n                  }}\n                >\n                  {(this.props.grouping && columnDef.field)\n                    ? <Draggable\n                      key={columnDef.tableData.id}\n                      draggableId={columnDef.tableData.id.toString()}\n                      index={index}>\n                      {(provided) => (\n                        <div\n                          ref={provided.innerRef}\n                          {...provided.draggableProps}\n                          {...provided.dragHandleProps}                          \n                        >\n                          {columnDef.title}\n                        </div>\n                      )}\n                    </Draggable>\n                    : columnDef.title\n                  }\n                </TableSortLabel>\n                : columnDef.title\n              }\n            </div>\n            {columnDef.filter && \n                    <div>\n                      {columnDef.filter}\n                    </div>\n\n            }\n          </div>\n        </TableCell>\n      ));\n    return mapArr;\n  }\n\n  renderActionsHeader() {\n    const localization = { ...TableHeader.defaultProps.localization, ...this.props.localization };\n    return (\n      <TableCell\n        key=\"key-actions-column\"\n        style={this.props.headerStyle}\n      >\n        <TableSortLabel>{localization.actions}</TableSortLabel>\n      </TableCell>\n    );\n  }\n  renderSelectionHeader() {\n    return (\n      <TableCell\n        padding=\"none\"\n        key=\"key-selection-column\"\n        style={this.props.headerStyle}\n      >\n        <Checkbox\n          indeterminate={this.props.selectedCount > 0 && this.props.selectedCount < this.props.dataCount}\n          checked={this.props.selectedCount === this.props.dataCount}\n          onChange={(event, checked) => this.props.onAllSelected && this.props.onAllSelected(checked)}\n        />\n      </TableCell>\n    );\n  }\n  render() {\n    const headers = this.renderHeader();\n    if (this.props.hasSelection && this.props.dataCount) {\n      headers.splice(0, 0, this.renderSelectionHeader());\n    }\n\n    if (this.props.showActionsColumn) {\n      if (this.props.actionsHeaderIndex >= 0) {\n        let endPos = 0;\n        if (this.props.hasSelection) {\n          endPos = 1;\n        }\n        headers.splice(this.props.actionsHeaderIndex + endPos, 0, this.renderActionsHeader());\n      } else if (this.props.actionsHeaderIndex === -1) {\n        headers.push(this.renderActionsHeader());\n      }\n    }\n\n    if (this.props.hasDetailPanel) {\n      headers.splice(0, 0,\n        <TableCell\n          padding=\"none\"\n          key=\"key-detail-panel-column\"\n          style={this.props.headerStyle}\n        />\n      );\n    }\n\n    this.props.columns\n      .filter(columnDef => columnDef.tableData.groupOrder > -1)\n      .forEach(columnDef => {\n        headers.splice(0, 0, <TableCell padding=\"checkbox\" key={\"key-group-header\" + columnDef.tableData.id} />);\n      });\n\n    return (\n      <TableHead>\n        <TableRow>\n          {headers}\n        </TableRow>\n      </TableHead>\n    );\n  }\n}\n\nTableHeader.defaultProps = {\n  dataCount: 0,\n  hasSelection: false,\n  headerStyle: {},\n  selectedCount: 0,\n  sorting: true,\n  localization: {\n    actions: \"Actions\" \n  },\n  orderBy: undefined,\n  orderDirection: \"asc\",\n  actionsHeaderIndex: 0\n};\n\nTableHeader.propTypes = {\n  columns: PropTypes.array.isRequired,\n  dataCount: PropTypes.number,\n  hasDetailPanel: PropTypes.bool.isRequired,\n  hasSelection: PropTypes.bool,\n  headerStyle: PropTypes.object,\n  localization: PropTypes.object,\n  selectedCount: PropTypes.number,\n  sorting: PropTypes.bool,\n  onAllSelected: PropTypes.func,\n  onOrderChange: PropTypes.func,\n  orderBy: PropTypes.number,\n  orderDirection: PropTypes.string,\n  actionsHeaderIndex: PropTypes.number,\n  showActionsColumn: PropTypes.bool,\n};\n\nexport default TableHeader;"
  },
  {
    "path": "test/fixture/react-class-jsx/expected/react-class.tsx",
    "content": "import * as React from \"react\";\nimport PropTypes from \"prop-types\";\nimport {\n  TableHead, TableRow, TableCell,\n  TableSortLabel, Checkbox\n} from \"@material-ui/core\";\nimport { Draggable } from \"react-beautiful-dnd\";\n\nclass TableHeader extends React.Component {\n\tpublic props: any;\n\n  renderHeader() {\n    const mapArr = this.props.columns.filter(columnDef => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1))\n      .map((columnDef, index) => (\n        <TableCell\n          key={columnDef.tableData.id}\n          align={[\"numeric\"].indexOf(columnDef.type) !== -1 ? \"right\" : \"left\"}\n\n          style={{ ...this.props.headerStyle, ...columnDef.headerStyle }}\n        >\n          <div className=\"displayFlex\">\n            <div>\n              {(columnDef.sort !== false && columnDef.sorting !== false && this.props.sorting)\n                ? <TableSortLabel\n                  active={this.props.orderBy === columnDef.tableData.id}\n                  direction={this.props.orderDirection || \"asc\"}\n                  onClick={() => {\n                    const orderDirection = columnDef.tableData.id !== this.props.orderBy ? \"asc\" : this.props.orderDirection === \"asc\" ? \"desc\" : \"asc\";\n                    this.props.onOrderChange(columnDef.tableData.id, orderDirection);\n                  }}\n                >\n                  {(this.props.grouping && columnDef.field)\n                    ? <Draggable\n                      key={columnDef.tableData.id}\n                      draggableId={columnDef.tableData.id.toString()}\n                      index={index}>\n                      {(provided) => (\n                        <div\n                          ref={provided.innerRef}\n                          {...provided.draggableProps}\n                          {...provided.dragHandleProps}                          \n                        >\n                          {columnDef.title}\n                        </div>\n                      )}\n                    </Draggable>\n                    : columnDef.title\n                  }\n                </TableSortLabel>\n                : columnDef.title\n              }\n            </div>\n            {columnDef.filter && \n                    <div>\n                      {columnDef.filter}\n                    </div>\n\n            }\n          </div>\n        </TableCell>\n      ));\n    return mapArr;\n  }\n\n  renderActionsHeader() {\n    const localization = { ...TableHeader.defaultProps.localization, ...this.props.localization };\n    return (\n      <TableCell\n        key=\"key-actions-column\"\n        style={this.props.headerStyle}\n      >\n        <TableSortLabel>{localization.actions}</TableSortLabel>\n      </TableCell>\n    );\n  }\n  renderSelectionHeader() {\n    return (\n      <TableCell\n        padding=\"none\"\n        key=\"key-selection-column\"\n        style={this.props.headerStyle}\n      >\n        <Checkbox\n          indeterminate={this.props.selectedCount > 0 && this.props.selectedCount < this.props.dataCount}\n          checked={this.props.selectedCount === this.props.dataCount}\n          onChange={(event, checked) => this.props.onAllSelected && this.props.onAllSelected(checked)}\n        />\n      </TableCell>\n    );\n  }\n  render() {\n    const headers = this.renderHeader();\n    if (this.props.hasSelection && this.props.dataCount) {\n      headers.splice(0, 0, this.renderSelectionHeader());\n    }\n\n    if (this.props.showActionsColumn) {\n      if (this.props.actionsHeaderIndex >= 0) {\n        let endPos = 0;\n        if (this.props.hasSelection) {\n          endPos = 1;\n        }\n        headers.splice(this.props.actionsHeaderIndex + endPos, 0, this.renderActionsHeader());\n      } else if (this.props.actionsHeaderIndex === -1) {\n        headers.push(this.renderActionsHeader());\n      }\n    }\n\n    if (this.props.hasDetailPanel) {\n      headers.splice(0, 0,\n        <TableCell\n          padding=\"none\"\n          key=\"key-detail-panel-column\"\n          style={this.props.headerStyle}\n        />\n      );\n    }\n\n    this.props.columns\n      .filter(columnDef => columnDef.tableData.groupOrder > -1)\n      .forEach(columnDef => {\n        headers.splice(0, 0, <TableCell padding=\"checkbox\" key={\"key-group-header\" + columnDef.tableData.id} />);\n      });\n\n    return (\n      <TableHead>\n        <TableRow>\n          {headers}\n        </TableRow>\n      </TableHead>\n    );\n  }\n}\n\nTableHeader.defaultProps = {\n  dataCount: 0,\n  hasSelection: false,\n  headerStyle: {},\n  selectedCount: 0,\n  sorting: true,\n  localization: {\n    actions: \"Actions\" \n  },\n  orderBy: undefined,\n  orderDirection: \"asc\",\n  actionsHeaderIndex: 0\n};\n\nTableHeader.propTypes = {\n  columns: PropTypes.array.isRequired,\n  dataCount: PropTypes.number,\n  hasDetailPanel: PropTypes.bool.isRequired,\n  hasSelection: PropTypes.bool,\n  headerStyle: PropTypes.object,\n  localization: PropTypes.object,\n  selectedCount: PropTypes.number,\n  sorting: PropTypes.bool,\n  onAllSelected: PropTypes.func,\n  onOrderChange: PropTypes.func,\n  orderBy: PropTypes.number,\n  orderDirection: PropTypes.string,\n  actionsHeaderIndex: PropTypes.number,\n  showActionsColumn: PropTypes.bool,\n};\n\nexport default TableHeader;"
  },
  {
    "path": "test/fixture/react-class-jsx/input/react-class.jsx",
    "content": "import * as React from \"react\";\nimport PropTypes from \"prop-types\";\nimport {\n  TableHead, TableRow, TableCell,\n  TableSortLabel, Checkbox\n} from \"@material-ui/core\";\nimport { Draggable } from \"react-beautiful-dnd\";\n\nclass TableHeader extends React.Component {\n    \n  renderHeader() {\n    const mapArr = this.props.columns.filter(columnDef => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1))\n      .map((columnDef, index) => (\n        <TableCell\n          key={columnDef.tableData.id}\n          align={[\"numeric\"].indexOf(columnDef.type) !== -1 ? \"right\" : \"left\"}\n\n          style={{ ...this.props.headerStyle, ...columnDef.headerStyle }}\n        >\n          <div className=\"displayFlex\">\n            <div>\n              {(columnDef.sort !== false && columnDef.sorting !== false && this.props.sorting)\n                ? <TableSortLabel\n                  active={this.props.orderBy === columnDef.tableData.id}\n                  direction={this.props.orderDirection || \"asc\"}\n                  onClick={() => {\n                    const orderDirection = columnDef.tableData.id !== this.props.orderBy ? \"asc\" : this.props.orderDirection === \"asc\" ? \"desc\" : \"asc\";\n                    this.props.onOrderChange(columnDef.tableData.id, orderDirection);\n                  }}\n                >\n                  {(this.props.grouping && columnDef.field)\n                    ? <Draggable\n                      key={columnDef.tableData.id}\n                      draggableId={columnDef.tableData.id.toString()}\n                      index={index}>\n                      {(provided) => (\n                        <div\n                          ref={provided.innerRef}\n                          {...provided.draggableProps}\n                          {...provided.dragHandleProps}                          \n                        >\n                          {columnDef.title}\n                        </div>\n                      )}\n                    </Draggable>\n                    : columnDef.title\n                  }\n                </TableSortLabel>\n                : columnDef.title\n              }\n            </div>\n            {columnDef.filter && \n                    <div>\n                      {columnDef.filter}\n                    </div>\n\n            }\n          </div>\n        </TableCell>\n      ));\n    return mapArr;\n  }\n\n  renderActionsHeader() {\n    const localization = { ...TableHeader.defaultProps.localization, ...this.props.localization };\n    return (\n      <TableCell\n        key=\"key-actions-column\"\n        style={this.props.headerStyle}\n      >\n        <TableSortLabel>{localization.actions}</TableSortLabel>\n      </TableCell>\n    );\n  }\n  renderSelectionHeader() {\n    return (\n      <TableCell\n        padding=\"none\"\n        key=\"key-selection-column\"\n        style={this.props.headerStyle}\n      >\n        <Checkbox\n          indeterminate={this.props.selectedCount > 0 && this.props.selectedCount < this.props.dataCount}\n          checked={this.props.selectedCount === this.props.dataCount}\n          onChange={(event, checked) => this.props.onAllSelected && this.props.onAllSelected(checked)}\n        />\n      </TableCell>\n    );\n  }\n  render() {\n    const headers = this.renderHeader();\n    if (this.props.hasSelection && this.props.dataCount) {\n      headers.splice(0, 0, this.renderSelectionHeader());\n    }\n\n    if (this.props.showActionsColumn) {\n      if (this.props.actionsHeaderIndex >= 0) {\n        let endPos = 0;\n        if (this.props.hasSelection) {\n          endPos = 1;\n        }\n        headers.splice(this.props.actionsHeaderIndex + endPos, 0, this.renderActionsHeader());\n      } else if (this.props.actionsHeaderIndex === -1) {\n        headers.push(this.renderActionsHeader());\n      }\n    }\n\n    if (this.props.hasDetailPanel) {\n      headers.splice(0, 0,\n        <TableCell\n          padding=\"none\"\n          key=\"key-detail-panel-column\"\n          style={this.props.headerStyle}\n        />\n      );\n    }\n\n    this.props.columns\n      .filter(columnDef => columnDef.tableData.groupOrder > -1)\n      .forEach(columnDef => {\n        headers.splice(0, 0, <TableCell padding=\"checkbox\" key={\"key-group-header\" + columnDef.tableData.id} />);\n      });\n\n    return (\n      <TableHead>\n        <TableRow>\n          {headers}\n        </TableRow>\n      </TableHead>\n    );\n  }\n}\n\nTableHeader.defaultProps = {\n  dataCount: 0,\n  hasSelection: false,\n  headerStyle: {},\n  selectedCount: 0,\n  sorting: true,\n  localization: {\n    actions: \"Actions\" \n  },\n  orderBy: undefined,\n  orderDirection: \"asc\",\n  actionsHeaderIndex: 0\n};\n\nTableHeader.propTypes = {\n  columns: PropTypes.array.isRequired,\n  dataCount: PropTypes.number,\n  hasDetailPanel: PropTypes.bool.isRequired,\n  hasSelection: PropTypes.bool,\n  headerStyle: PropTypes.object,\n  localization: PropTypes.object,\n  selectedCount: PropTypes.number,\n  sorting: PropTypes.bool,\n  onAllSelected: PropTypes.func,\n  onOrderChange: PropTypes.func,\n  orderBy: PropTypes.number,\n  orderDirection: PropTypes.string,\n  actionsHeaderIndex: PropTypes.number,\n  showActionsColumn: PropTypes.bool,\n};\n\nexport default TableHeader;"
  },
  {
    "path": "test/fixture/react-jsx-self-closing-element/expected/react-self-closing-element.tsx",
    "content": "import * as React from \"react\";\n\n/**\n * This example makes sure that .js is converted to .tsx when the only JSX\n * element in the file is self-closing\n */\nexport const MyComponent = () => {\n  return <div/>;\n};"
  },
  {
    "path": "test/fixture/react-jsx-self-closing-element/input/react-self-closing-element.js",
    "content": "import * as React from \"react\";\n\n/**\n * This example makes sure that .js is converted to .tsx when the only JSX\n * element in the file is self-closing\n */\nexport const MyComponent = () => {\n  return <div/>;\n};"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/another-sub-class.ts",
    "content": "import DefaultExportClass from \"./default-export-class\";\n\nexport class AnotherSubClass extends DefaultExportClass {\n\tpublic anotherSubClassProp: any;\n\n\tconstructor() {\n\t\tthis.defaultExportClassProp = 45;  // from superclass\n\t\tthis.anotherSubClassProp = 10;\n\t}\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/default-export-class.ts",
    "content": "class DefaultExportClass {\n\tpublic defaultExportClassProp: any;\n\n\tconstructor() {\n\t\tthis.defaultExportClassProp = 1;\n\t}\n\n}\n\nexport default DefaultExportClass;"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/my-class.ts",
    "content": "import { MySuperClass } from './my-super-class';\n\nexport class MyClass extends MySuperClass {\n\tpublic myClassProp1: any;\n\tpublic myClassProp2: any;\n\tpublic myClassProp3: any;\n\n\tconstructor() {\n\t\tthis.mySuperClassProp = 99;\n\t\tthis.myClassProp1 = 42;\n\t\tthis.doSomething();  // should not become a property\n\t\tthis.mySuperclassMethod();  // should not become a property as it is a method in the superclass\n\t}\n\n\tdoSomething() {\n\t\tthis.myClassProp2 = 78;\n\t\tconsole.log( this.myClassProp3 );\n\t}\n\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/my-sub-class.ts",
    "content": "import { MyClass } from \"./my-class\";\n\nexport class MySubClass extends MyClass {\n\tpublic mySubClassProp: any;\n\n\tconstructor() {\n\t\tthis.mySuperClassProp = 42;  // from superclass's superclass - should not be added as a prop\n\t\tthis.myClassProp1 = 43;  // from superclass - should not be added as a prop\n\t\tthis.mySubClassProp = 1;\n\t\tthis.mySuperclassMethod();  // should not be added as a property as it exists two superclasses up\n\t}\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/my-super-class.ts",
    "content": "export class MySuperClass {\n\tpublic mySuperClassProp: any;\n\n\tconstructor() {\n\t\tthis.mySuperclassMethod();  // should not be added as a property\n\t}\n\n\tmySuperclassMethod() {\n\t\tthis.mySuperClassProp = 10;\n\t}\n\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/superclass-in-node-modules.ts",
    "content": "import { Subject } from 'rxjs';\n\nexport class MySubClassWithSuperClassInNodeModules extends Subject {\n\tpublic myProp: any;\n\n\tmySuperclassMethod() {\n\t\tthis.myProp = 10;\n\t}\n\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/expected/two-classes.ts",
    "content": "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 subProp: any;\n\n\tsomeMethod() {\n\t\tthis.superProp = 2;\n\t\tthis.subProp = 2;\n\t}\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/input/another-sub-class.js",
    "content": "import DefaultExportClass from \"./default-export-class\";\n\nexport class AnotherSubClass extends DefaultExportClass {\n\tconstructor() {\n\t\tthis.defaultExportClassProp = 45;  // from superclass\n\t\tthis.anotherSubClassProp = 10;\n\t}\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/input/default-export-class.js",
    "content": "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",
    "content": "import { MySuperClass } from './my-super-class';\n\nexport class MyClass extends MySuperClass {\n\n\tconstructor() {\n\t\tthis.mySuperClassProp = 99;\n\t\tthis.myClassProp1 = 42;\n\t\tthis.doSomething();  // should not become a property\n\t\tthis.mySuperclassMethod();  // should not become a property as it is a method in the superclass\n\t}\n\n\tdoSomething() {\n\t\tthis.myClassProp2 = 78;\n\t\tconsole.log( this.myClassProp3 );\n\t}\n\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/input/my-sub-class.js",
    "content": "import { MyClass } from \"./my-class\";\n\nexport class MySubClass extends MyClass {\n\tconstructor() {\n\t\tthis.mySuperClassProp = 42;  // from superclass's superclass - should not be added as a prop\n\t\tthis.myClassProp1 = 43;  // from superclass - should not be added as a prop\n\t\tthis.mySubClassProp = 1;\n\t\tthis.mySuperclassMethod();  // should not be added as a property as it exists two superclasses up\n\t}\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/input/my-super-class.js",
    "content": "export class MySuperClass {\n\n\tconstructor() {\n\t\tthis.mySuperclassMethod();  // should not be added as a property\n\t}\n\n\tmySuperclassMethod() {\n\t\tthis.mySuperClassProp = 10;\n\t}\n\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/input/package.json",
    "content": "{\n  \"devDependencies\": {},\n  \"dependencies\": {\n    \"rxjs\": \"^6.2.2\"\n  }\n}\n"
  },
  {
    "path": "test/fixture/superclass-subclass/input/superclass-in-node-modules.js",
    "content": "import { Subject } from 'rxjs';\n\nexport class MySubClassWithSuperClassInNodeModules extends Subject {\n\n\tmySuperclassMethod() {\n\t\tthis.myProp = 10;\n\t}\n\n}"
  },
  {
    "path": "test/fixture/superclass-subclass/input/two-classes.js",
    "content": "class Super {\n\tsomeMethod() {\n\t\tthis.superProp = 1;\n\t}\n}\n\n\nclass Sub extends Super {\n\tsomeMethod() {\n\t\tthis.superProp = 2;\n\t\tthis.subProp = 2;\n\t}\n}"
  },
  {
    "path": "test/fixture/superclass-subclass-node-modules-not-installed/expected/superclass-in-node-modules.ts",
    "content": "import { SomeSuperclass } from 'some-not-installed-module';\n\nexport class MyClassWithSuperClassInNodeModules extends SomeSuperclass {\n\tpublic myProp: any;\n\n\tmyMethod() {\n\t\tthis.myProp = 10;\n\t}\n\n}"
  },
  {
    "path": "test/fixture/superclass-subclass-node-modules-not-installed/input/superclass-in-node-modules.js",
    "content": "import { SomeSuperclass } from 'some-not-installed-module';\n\nexport class MyClassWithSuperClassInNodeModules extends SomeSuperclass {\n\n\tmyMethod() {\n\t\tthis.myProp = 10;\n\t}\n\n}"
  },
  {
    "path": "test/fixture/typescript-class/expected/declarations-in-superclass.ts",
    "content": "class SuperTypeScriptClass {\n\tpublic superProp: any;  // *declaration* that should not be added to subclass\n}\n\nclass SubTypeScriptClass extends SuperTypeScriptClass {\n\tpublic subProp: any;\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.superProp = 1;  // should not be added as a declaration in this class\n\t\tthis.subProp = 2;    // *should* be filled in as it is currently missing in this class and its superclass\n\t}\n}\n\nclass SubSubTypeScriptClass extends SubTypeScriptClass {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.superProp = 1;  // should not be added as a declaration in this class as it is declared 2 superclasses up\n\t}\n}"
  },
  {
    "path": "test/fixture/typescript-class/expected/typescript-class.ts",
    "content": "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",
    "content": "import { TypescriptClass } from \"./typescript-class\";\n\nexport class TypescriptSubClass extends TypescriptClass {\n\tpublic prop2: any;\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.prop2 = 1;\n\t}\n}"
  },
  {
    "path": "test/fixture/typescript-class/input/declarations-in-superclass.ts",
    "content": "class SuperTypeScriptClass {\n\tpublic superProp: any;  // *declaration* that should not be added to subclass\n}\n\nclass SubTypeScriptClass extends SuperTypeScriptClass {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.superProp = 1;  // should not be added as a declaration in this class\n\t\tthis.subProp = 2;    // *should* be filled in as it is currently missing in this class and its superclass\n\t}\n}\n\nclass SubSubTypeScriptClass extends SubTypeScriptClass {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.superProp = 1;  // should not be added as a declaration in this class as it is declared 2 superclasses up\n\t}\n}"
  },
  {
    "path": "test/fixture/typescript-class/input/typescript-class.ts",
    "content": "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",
    "content": "import { TypescriptClass } from \"./typescript-class\";\n\nexport class TypescriptSubClass extends TypescriptClass {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.prop2 = 1;\n\t}\n}"
  },
  {
    "path": "test.ts",
    "content": "import { Project } from \"ts-morph\";\n\nconst tsAstProject = new Project();\nconst sourceFile = tsAstProject.createSourceFile('testfile.js', getSourceText());\n\n// Just calling the below method is what causes the problem when moving later. \n// If this line is commented out, the move succeeds.\nsourceFile.getClass( 'TableHeader' )!;\n\nsourceFile.move('testfile.tsx');\n\n\nfunction getSourceText() {\n    return `\n        class TableHeader extends React.Component {\n            renderHeader() {\n                const mapArr = this.props.columns\n                    .map(columnDef => (\n                        <div>\n                            {(columnDef.sort !== false)\n                                ? 'test'\n                                : 'title'\n                            }\n                        </div>\n                    ));\n\n                return mapArr;\n            }\n        }\n    `;\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t/* Basic Options */\n\t\t\"target\": \"es2017\",                       /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */\n\t\t\"module\": \"commonjs\",                     /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */\n\t\t\"skipLibCheck\": true,\n\t\t// \"lib\": [],                             /* Specify library files to be included in the compilation:  */\n\t\t// \"allowJs\": true,                       /* Allow javascript files to be compiled. */\n\t\t// \"checkJs\": true,                       /* Report errors in .js files. */\n\t\t// \"jsx\": \"preserve\",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */\n\t\t\"declaration\": true,                      /* Generates corresponding '.d.ts' file. */\n\t\t// \"sourceMap\": true,                     /* Generates corresponding '.map' file. */\n\t\t// \"outFile\": \"./\",                       /* Concatenate and emit output to single file. */\n\t\t\"outDir\": \"./dist\",                       /* Redirect output structure to the directory. */\n\t\t// \"rootDir\": \"./\",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */\n\t\t// \"removeComments\": true,                /* Do not emit comments to output. */\n\t\t// \"noEmit\": true,                        /* Do not emit outputs. */\n\t\t// \"importHelpers\": true,                 /* Import emit helpers from 'tslib'. */\n\t\t// \"downlevelIteration\": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */\n\t\t// \"isolatedModules\": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */\n\t\t\"esModuleInterop\": true,\n\n\t\t/* Strict Type-Checking Options */\n\t\t\"strict\": true,                           /* Enable all strict type-checking options. */\n\t\t// \"noImplicitAny\": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */\n\t\t// \"strictNullChecks\": true,              /* Enable strict null checks. */\n\t\t// \"noImplicitThis\": true,                /* Raise error on 'this' expressions with an implied 'any' type. */\n\t\t// \"alwaysStrict\": true,                  /* Parse in strict mode and emit \"use strict\" for each source file. */\n\n\t\t/* Additional Checks */\n\t\t// \"noUnusedLocals\": true,                /* Report errors on unused locals. */\n\t\t// \"noUnusedParameters\": true,            /* Report errors on unused parameters. */\n\t\t\"noImplicitReturns\": true,                /* Report error when not all code paths in function return a value. */\n\t\t\"noFallthroughCasesInSwitch\": true,       /* Report errors for fallthrough cases in switch statement. */\n\n\t\t/* Module Resolution Options */\n\t\t// \"moduleResolution\": \"node\",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */\n\t\t\"baseUrl\": \"./src\",                       /* Base directory to resolve non-absolute module names. */\n\t\t// \"paths\": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */\n\t\t// \"rootDirs\": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */\n\t\t// \"typeRoots\": [],                       /* List of folders to include type definitions from. */\n\t\t// \"types\": [],                           /* Type declaration files to be included in compilation. */\n\t\t// \"allowSyntheticDefaultImports\": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */\n\n\t\t/* Source Map Options */\n\t\t// \"sourceRoot\": \"./\",                    /* Specify the location where debugger should locate TypeScript files instead of source locations. */\n\t\t// \"mapRoot\": \"./\",                       /* Specify the location where debugger should locate map files instead of generated locations. */\n\t\t\"inlineSourceMap\": true,               /* Emit a single file with source maps instead of having a separate file. */\n\t\t\"inlineSources\": true                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */\n\n\t\t/* Experimental Options */\n\t\t// \"experimentalDecorators\": true,        /* Enables experimental support for ES7 decorators. */\n\t\t// \"emitDecoratorMetadata\": true,         /* Enables experimental support for emitting type metadata for decorators. */\n\t},\n\t\"include\": [\n\t\t\"src/**/*.ts\"\n\t]\n}"
  }
]