# Ts Morph > Certain nodes in TypeScript can be ambient. For example, all nodes within a declaration file are ambient. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/ambient.md # Path: docs/details/ambient.md --- title: Ambient --- ## Ambient Certain nodes in TypeScript can be ambient. For example, all nodes within a declaration file are ambient. ### Testing if ambient Use `isAmbient()`: ```ts classDeclaration.isAmbient(); // returns: boolean ``` This will do several checks to see if it's an ambient declaration. ### `declare` keyword Check for the `declare` keyword: ```ts classDeclaration.hasDeclareKeyword(); // returns: boolean ``` Or get the `declare` keyword if it exists: ```ts classDeclaration.getDeclareKeyword(); ``` Or set if it has a `declare` keyword: ```ts classDeclaration.setHasDeclareKeyword(true); classDeclaration.setHasDeclareKeyword(false); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/async.md # Path: docs/details/async.md --- title: Async --- ## Async Certain nodes in TypeScript can have an `async` keyword modifier. ### Is async A node can be tested if it's async using the `isAsync()` method: ```ts functionDeclaration.isAsync(); // returns: boolean ``` ### `async` keyword Get the `async` keyword if it exists: ```ts functionDeclaration.getAsyncKeyword(); ``` ### Set async Set if the declaration is async using `setIsAsync`: ```ts functionDeclaration.setIsAsync(true); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/classes.md # Path: docs/details/classes.md --- title: Classes --- ## Class Declarations Class declarations can be retrieved from source files, namespaces, or function bodies: ```ts const classes = sourceFile.getClasses(); const class1 = sourceFile.getClass("Class1"); const firstClassWithConstructor = sourceFile.getClass(c => c.getConstructors().length > 0); ``` ### Name It's important to note that class declarations may not have a name. For example: ```ts export default class { // etc... } ``` For this reason, the methods like `.getName()` and `.getNameNode()` are nullable on `ClassDeclaration`. ### Add/Insert Add or insert classes to a source file, namespace, or function like declarations by calling `addClass()`, `addClasses()`, `insertClass()`, or `insertClasses()`. ```ts const classDeclaration = sourceFile.addClass({ name: "ClassName", }); ``` ### Remove Call `.remove()`: ```ts classDeclaration.remove(); ``` ### Extends expression Will return [`ExpressionWithTypeArguments | undefined`](expressions): ```ts const extendsExpression = classDeclaration.getExtends(); ``` Set the extends expression: ```ts classDeclaration.setExtends("BaseClass"); ``` Remove it: ```ts classDeclaration.removeExtends(); ``` ### Implements expressions Will return [`ExpressionWithTypeArguments[]`](expressions): ```ts const implementsExpressions = classDeclaration.getImplements(); ``` Add or insert implements expressions: ```ts classDeclaration.addImplements("Named"); classDeclaration.addImplements(["Named", "Aged"]); classDeclaration.insertImplements(1, "Named"); classDeclaration.insertImplements(1, ["Named", "Aged"]); ``` Remove an expression: ```ts classDeclaration.removeImplements(0); // index classDeclaration.removeImplements(classDeclaration.getImplements()[0]); // node ``` ### Base Types Get the base types: ```ts const baseTypes = classDeclaration.getBaseTypes(); // returns: Type[] ``` This is useful to use if you don't know if the class could possibly extend a mixin or a class. ### Base Class Get the base class: ```ts const baseClass = classDeclaration.getBaseClass(); // returns: ClassDeclaration | undefined ``` Note: This is not a useful method to use if the base could possibly be a mixin. If you expect mixins, then use `.getBaseTypes()`. ### Derived Classes Will return all the class declarations that derive from the current class: ```ts const derivedClasses = classDeclaration.getDerivedClasses(); ``` ### Constructor Constructors can be retreived via `getConstructors`. This returns all the constructors in an ambient context, but will only return the implementation constructor otherwise. ```ts const constructors = classDeclaration.getConstructors(); ``` Add or insert a constructor or constructors by calling `addConstructor()`, `addConstructors()`, `insertConstructor()`, or `insertConstructors()`. ```ts const ctor = classDeclaration.addConstructor({ /* options like parameters may go here */ }); ``` ### Methods Get instance methods: ```ts const instanceMethods = classDeclaration.getInstanceMethods(); const myMethod = classDeclaration.getInstanceMethod("myMethod"); const firstMethodWith2Params = classDeclaration.getInstanceMethod(m => m.getParameters().length === 2); ``` Get the static methods: ```ts const staticMethods = classDeclaration.getStaticMethods(); const myStaticMethod = classDeclaration.getStaticMethod("myMethod"); const firstStaticMethodWith2Params = classDeclaration.getStaticMethod(m => m.getParameters().length === 2); ``` #### Add/Insert Add or insert methods by using `insertMethods()`, `insertMethod`, `addMethod`, or `addMethods`: ```ts const method = classDeclaration.addMethod({ isStatic: true, name: "myMethod", returnType: "string" }); ``` #### Remove Call `.remove()`: ```ts method.remove(); ``` ### Properties Get the instance properties (includes parameter properties): ```ts const instanceProperties = classDeclaration.getInstanceProperties(); const myProperty = classDeclaration.getInstanceProperty("myProperty"); const myStringProperty = classDeclaration.getInstanceProperty(p => Node.isPropertyDeclaration(p) && p.getType().getText() === "string"); ``` Get the static properties: ```ts const staticProperties = classDeclaration.getStaticProperties(); const myStaticProperty = classDeclaration.getStaticProperty("myStaticProperty"); const myStaticStringProperty = classDeclaration.getStaticProperty(p => Node.isPropertyDeclaration(p) && p.getType().getText() === "string"); ``` #### Add/Insert Add or insert properties by using `insertProperties()`, `insertProperty`, `addProperty`, or `addProperties`: ```ts const property = classDeclaration.addProperty({ isStatic: true, name: "prop", type: "string", }); ``` Add or insert get accessors by using `insertGetAccessors()`, `insertGetAccessor`, `addGetAccessor`, or `addGetAccessors`: ```ts const getAccessor = classDeclaration.addGetAccessor({ name: "someNumber", returnType: "number", statements: ["return 5;"], }); ``` Add or insert set accessors by using `insertSetAccessors()`, `insertSetAccessor`, `addSetAccessor`, or `addSetAccessors`: ```ts const setAccessor = classDeclaration.addSetAccessor({ name: "someNumber", parameters: [{ name: "value", type: "number" }], statements: ["_someNumber = value;"], }); ``` #### Remove Call `.remove()`: ```ts propertyDeclaration.remove(); ``` ### Get members Get all the members regardless of whether static or instance: ```ts const allMembers = classDeclaration.getMembers(); ``` Get instance members including parameter properties: ```ts const instanceMembers = classDeclaration.getInstanceMembers(); ``` Get static members: ```ts const staticMembers = classDeclaration.getStaticMembers(); ``` ### Extracting an Interface An interface declaration structure can be extracted from a class by calling `classDeclaration.extractInterface()`. For example: ```ts // the passed in name is optional and defaults to the class name const interfaceStructure = classDeclaration.extractInterface(`I${classDeclaration.getName()}`); ``` Alternatively the static interface declaration structure of a class can be extracted by calling: ```ts const interfaceStructure = classDeclaration.extractStaticInterface(`${classDeclaration.getName()}Static`); ``` ## Abstract Nodes on a class may be abstract. Get if it's abstract: ```ts method.isAbstract(); // returns: boolean ``` Get the abstract keyword: ```ts method.getAbstractKeyword(); // returns: node | undefined ``` Set if abstract: ```ts method.setIsAbstract(true); // set as abstract method.setIsAbstract(false); // set as not abstract ``` ## Constructors Constructors implement common functions found on function like declarations, but also include a scope. ## Methods Explore the functionality available via auto-complete. ## Properties Explore the functionality available via auto-complete. ## Get Accessors If it exists, get the corresponding set accessor: ```ts const setAccessor = getAccessor.getSetAccessor(); ``` ## Set Accessors If it exists, get the corresponding get accessor: ```ts const getAccessor = setAccessor.getGetAccessor(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/comment-ranges.md # Path: docs/details/comment-ranges.md --- title: Comment Ranges --- ## Comment Ranges See [Comments](comments.md). --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/comments.md # Path: docs/details/comments.md --- title: Comments --- ## Comments _ts-morph_ parses out certain kinds of comments to make them easier to work with. This breaks away from the behaviour in the compiler API because although comments are not very important for compiling, they can be important for programmatic refactoring tasks. - **Comment Ranges** - Comments the compiler api parses out. - **Comment Nodes** - Comments ts-morph parses out in certain scenarios. They extend from `ts.Node`. ## Comment Ranges Comment ranges are not part of the AST and are generated on request. - **Leading:** Comments that preceed the node after the previous significant token—between `node.getPos()` and `node.getStart()`. - **Trailing:** Comments following the node before the next siginificant token or newline. WARNING: Since comments are generated on demand and not part of the AST, using one after a subsequent manipulation to the source file will throw an error. ### Retrieving Leading and trailing comment ranges can be retrieved from any node by calling: ```ts const leadingComments = node.getLeadingCommentRanges(); const trailingComments = node.getTrailingCommentRanges(); ``` ### Information Once you have a comment range, there are several self explanatory methods: - `.getKind()` - Returns `SyntaxKind.SingleLineCommentTrivia` or `SyntaxKind.MultiLineCommentTrivia`. - `.getPos()` - Position. - `.getEnd()` - End position. - `.getWidth()` - Width (`end - pos`) - `.getText()` - Text. - `.wasForgotten()` - Returns true if the node was forgotten because a manipulation occured to the source file or its associated node was forgotten. ### More Details Read more about comments in the compiler API documentation here: https://github.com/Microsoft/TypeScript/wiki/Architectural-Overview#trivia ## Comment Nodes These are nodes ts-morph parses out. They do not include all comments, but only the first comment on a line if there are no significant tokens on that line and only in certain contexts (ex. source file statements, class declaration body, function body, namespace body, etc..). For example, in the following scenario: ```ts setup: let functionCall: any; // do something functionCall(); // ok ``` ...only the first comment will be parsed out. The second comment is considered a trailing comment range of the function call. ### Retrieving Comment ranges can be retrieved using various `#getXWithComments()` methods. For example: ```ts setup: let classDec: any, interfaceDec: any; sourceFile.getStatementsWithComments(); classDec.getMembersWithComments(); interfaceDec.getMembersWithComments(); objectLiteralExpression.getPropertiesWithComments(); ``` They also may appear in the results of `#getChildren()` in certain scenarios. For example: ```ts const children = sourceFile.getChildSyntaxListOrThrow().getChildren(); ``` This extends the behaviour of the compiler API. ### Why? The reason this is done is to make inserting before or after comments possible. For example, given the following source file: ```ts setup: let functionCall: any; // 1 // 2 functionCall(); ``` It is possible to insert a statement after the first comment by specifying `sourceFile.insertStatement(1, "// new comment");`, which would modify the source file to be: ```ts setup: let functionCall: any; // 1 // new comment // 2 functionCall(); ``` ### Removing Comments To remove a comment node, call `#remove()` on it: ```ts sourceFile.getStatementsWithComments()[0].remove(); ``` This will remove the comment and any of its trailing comment nodes. ### Trailing comment ranges of comment nodes Comment nodes may have trailing comment ranges. For example, given the following source file: ```ts /* 1 */ // 2 ``` ...the text `/* 1 */` is considered the comment node, while `// 2` is the trailing comment range. `// 2` can be retrieved with the following code: ```ts const secondComment = sourceFile.getStatementsWithComments()[0].getTrailingCommentRanges()[0]; ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/decorators.md # Path: docs/details/decorators.md --- title: Decorators --- ## Decorators Decorators can be retrieved from class related nodes by calling the `getDecorators()` method. ```ts const decorators = classDeclaration.getDecorators(); ``` ### Name Get the name or fully qualified name of a decorator by using the `getName()` or `getFullName()` functions respectively. For example, given the following code: ```ts setup: const obj: { decorator: any; }; @obj.decorator class Identifier { } ``` The following happens: ```ts decorator.getName(); // "decorator" decorator.getFullName(); // "obj.decorator" ``` ### Decorator factory Decorators with parenthesis (ex. `@decorator(3)`) are decorator factories, while decorators without (ex. `@decorator`) are not. ```ts decorator.isDecoratorFactory(); // returns: boolean ``` Set as a decorator factory or not: ```ts decorator.setIsDecoratorFactory(true); ``` ### Arguments Get the decorator's arguments by calling `.getArguments()`: ```ts const args = decorator.getArguments(); // returns: Expression[] ``` Add and insert via `.addArgument(...)`, `.insertArguments(...)`, `.addArgument(...)`, or `.addArguments(...)`. ```ts const args = decorator.insertArguments(1, ["5", "6"]); ``` And remove them by calling `.removeArgument()`: ```ts setup: const args: any; // specify the index decorator.removeArgument(0); // or specify the argument node decorator.removeArgument(args[0]); ``` ### Type arguments Get the decorator's type arguments by calling `.getTypeArguments()`: ```ts const typeArgs = decorator.getTypeArguments(); // returns: TypeNode[] ``` Add and insert via `.insertTypeArgument(...)`, `.insertTypeArguments(...)`, `.addTypeArgument(...)`, or `.addTypeArguments(...)`. ```ts const typeArgs = decorator.insertTypeArguments(1, ["string", "number"]); ``` And remove them by calling `.removeTypeArgument()`: ```ts setup: let typeArgs: TypeNode[]; // specify the index decorator.removeTypeArgument(0); // or specify the type argument node decorator.removeTypeArgument(typeArgs[0]); ``` ### Call expression Decorator factories are call expressions. Get the call expression by calling: ```ts const callExpression = decorator.getCallExpression(); // returns: CallExpression | undefined ``` ### Add/Insert decorators Decorators can be added or inserted by calling `addDecorator(decorator)`, `addDecorators(decorators)`, `insertDecorator(index, decorator)`, or `insertDecorators(index, decorators)`. For example: ```ts classDeclaration.addDecorator({ name: "MyDecorator", arguments: ["3", `"some string"`], }); ``` ### Remove decorators Call `.remove()`: ```ts decorator.remove(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/documentation.md # Path: docs/details/documentation.md --- title: JS Docs --- ## JS Docs Certain nodes can have JS docs. For example: ```ts setup: interface Person {} /** * Gets the name. * @param person - Person to get the name from. */ function getName(person: Person) { // ... } ``` ### Get all JS doc nodes Get all the JS doc nodes by using `getJsDocs()`: ```ts functionDeclaration.getJsDocs(); // returns: JSDoc[] ``` ### Add/insert docs Add or insert JS doc comments using the `addJsDoc()`, `addJsDocs()`, `insertJsDoc()`, and `insertJsDocs()` methods. For example: ```ts // adds /** Some description... */ const docNode = classDeclaration.addJsDoc({ description: "Some description...", }); // or to force it to be multi-line, add a newline to the front of the string classDeclaration.addJsDoc({ description: "\nSome description...", }); // or with tags classDeclaration.addJsDoc({ description: "Some description...", tags: [{ tagName: "param", text: "value - My value.", }], }); ``` ### JSDoc Nodes Get the description: ```ts // Getting the node from the example at the top of this file. const jsDoc = functionDeclaration.getJsDocs()[0]; jsDoc.getDescription(); // returns string: "Gets the name." ``` Get the tags: ```ts const tags = jsDoc.getTags(); tags[0].getText(); // "@param person - Person to get the name from." ``` Get the inner text (the text without the surrounding comment): ```ts jsDoc.getInnerText(); // "Gets the name.\n@param person - Person to get the name from." ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/enums.md # Path: docs/details/enums.md --- title: Enums --- ## Enums Enums can be retrieved from source files, namespaces, or function bodies: ```ts const enums = sourceFile.getEnums(); const enum1 = sourceFile.getEnum("Enum1"); const enum2 = sourceFile.getEnum(e => e.getMembers().length === 5); ``` ### Add/Insert Add or insert enums to a source file, namespace, or function like declarations by calling `addEnum()`, `addEnums()`, `insertEnum()`, or `insertEnums()`. ```ts const enumDeclaration = sourceFile.addEnum({ name: "EnumName", members: [{ name: "member", }], }); ``` ### Remove Call `.remove()`: ```ts enumDeclaration.remove(); ``` ### Get all members Use `getMembers()`: ```ts const members = enumDeclaration.getMembers(); ``` ### Get member Use `getMember()`: ```ts const member1 = enumDeclaration.getMember("member1"); const member2 = enumDeclaration.getMember(m => m.getValue() === 1); ``` ### Add/Insert member Members can be added by calling `addMember()`, `addMembers()`, `insertMember()`, or `insertMembers()`: ```ts const member = enumDeclaration.addMember({ name: "newMember", value: 10, }); ``` ### Const enum Check if it's a const enum via `isConstEnum()`: ```ts enumDeclaration.isConstEnum(); // returns: boolean ``` Get the `const` keyword via `getConstKeyword()`: ```ts enumDeclaration.getConstKeyword(); // returns: Node | undefined ``` Set if it's a const enum via `setIsConstEnum(value)`: ```ts enumDeclaration.setIsConstEnum(true); enumDeclaration.setIsConstEnum(false); ``` ## Enum Members ```ts const member = enumDeclaration.getMember("member"); ``` ### Value The value can be retrieved whether it is implicitly or explicitly defined: ```ts enumMember.getValue(); // returns: string | number ``` It can also be set to a number: ```ts enumMember.setValue(5); ``` Or a string: ```ts enumMember.setValue("string value"); ``` ### Removing Call `remove()` on it: ```ts enumMember.remove(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/exports.md # Path: docs/details/exports.md --- title: Export --- ## Export Certain nodes in TypeScript can be [exported](https://www.typescriptlang.org/docs/handbook/modules.html). ### Is export Use `isExported()`, `isNamedExport()`, or `isDefaultExport()` methods: ```ts functionDeclaration.isExported(); // returns: boolean functionDeclaration.isNamedExport(); // returns: boolean functionDeclaration.isDefaultExport(); // returns: boolean ``` ### `export` and `default` keyword Use the `has` methods to check for the `export` and `default` keywords: ```ts functionDeclaration.hasExportKeyword(); // returns: boolean functionDeclaration.hasDefaultKeyword(); // returns: boolean ``` And use the `get` methods to get the keywords: ```ts functionDeclaration.getExportKeyword(); functionDeclaration.getDefaultKeyword(); ``` ### Setting as default export Use `setIsDefaultExport` to set a node as being a default export or not: ```ts functionDeclaration.setIsDefaultExport(true); // be one functionDeclaration.setIsDefaultExport(false); // don't be one ``` Note: This will throw an exception if the node's parent is not a source file. ### Setting as export Use `setIsExported` to set a node as being a named export if the parent is a source file or an export of a namespace if the parent is a namespace: ```ts functionDeclaration.setIsExported(true); // be one functionDeclaration.setIsExported(false); // don't be one ``` ### Get default export symbol If it exists, the default export symbol can be retrieved from source file or module: ```ts const defaultExportSymbol = sourceFile.getDefaultExportSymbol(); // returns: Symbol | undefined ``` ### Remove default export Use: ```ts sourceFile.removeDefaultExport(); ``` Note: This is safe to call even when there is no default export. ### Getting Exported Declarations The exported declarations of a file or module can be retrieved via `.getExportedDeclarations()`. This will return a map keyed on the export name with a value of the exported declarations for that name. For example, given the following setup: ```ts // main.ts export * from "./classes"; export { Interface1 as AliasedInterface } from "./interfaces"; namespace MergedNamespace { let t; } namespace MergedNamespace { let u; } export { MergedNamespace }; export default 5; // classes.ts export * from "./Class1"; export * from "./Class2"; // Class1.ts export class Class1 {} // Class2.ts export class Class2 {} // interfaces.ts export interface Interface1 {} export interface Interface2 {} ``` The following code: ```ts import { ExportedDeclarations, Project } from "ts-morph"; const project = new Project(); project.addSourceFilesAtPaths("**/*.ts"); const mainFile = project.getSourceFileOrThrow("main.ts"); for (const [name, declarations] of mainFile.getExportedDeclarations()) console.log(`${name}: ${declarations.map(d => d.getText()).join(", ")}`); ``` Outputs the following: ``` Class1: export class Class1 {} Class2: export class Class2 {} AliasedInterface: export interface Interface1 {} MergedNamespace: namespace MergedNamespace { let t; }, namespace MergedNamespace { let u; } default: 5 ``` ## Export Declarations Export declarations look like this: ```ts setup: class OtherClass {} export { MyClass } from "./other-file"; export * from "./some-file"; export { OtherClass }; ``` Get the export declarations by calling: ```ts const exportDeclarations = sourceFile.getExportDeclarations(); // or to get the first one that matches a condition const exportDeclaration = sourceFile.getExportDeclaration(d => d.hasNamedExports()); const exportDecForModule = sourceFile.getExportDeclaration("module-specifier-text"); // tell if it has named exports exportDeclaration.hasNamedExports(); // or if it's a namespace export exportDeclaration.isNamespaceExport(); // get/set the module specifier exportDeclaration.getModuleSpecifier(); // returns: StringLiteral | undefined exportDeclaration.getModuleSpecifierValue(); // returns: string | undefined exportDeclaration.setModuleSpecifier("./new-file"); exportDeclaration.setModuleSpecifier(sourceFile); exportDeclaration.removeModuleSpecifier(); exportDeclaration.hasModuleSpecifier(); // returns: boolean exportDeclaration.isModuleSpecifierRelative(); // if the module specifier starts with ./ or ../ exportDeclaration.getModuleSpecifierSourceFile(); // returns: SourceFile | undefined ``` ### Add/Insert Add or insert use `insertExportDeclaration`, `insertExportDeclarations`, `addExportDeclaration`, or `addExportDeclarations`: ```ts const exportDeclaration = sourceFile.addExportDeclaration({ namedExports: ["MyClass"], moduleSpecifier: "./file", }); ``` ### Remove Call `.remove()`: ```ts exportDeclaration.remove(); ``` ### To Namespace Export Given an export declaration with named exports: ```ts export { Export1, Export2, Export3 } from "./other-file"; ``` Calling `exportDeclaration.toNamespaceExport();` will change the code to the following: ```ts export * from "./other-file"; ``` ### Named Exports Get the named exports from an export declaration: ```ts const namedExports = exportDeclaration.getNamedExports(); ``` Adding or inserting named exports can be done via the `addNamedExport`, `addNamedExports`, `insertNamedExport`, or `insertNamedExports` methods. ```ts const namedExport = exportDeclaration.addNamedExport({ name: "MyClass", alias: "MyAliasName", // alias is optional }); // or exportDeclaration.addNamedExport("MyClass"); ``` Removing one named export: ```ts namedExport.remove(); ``` #### Export specifier Export specifiers are the individual named exports. ##### Name ```ts namedExport.getNameNode(); // returns: Identifier namedExport.setName("NewName"); ``` ##### Alias ```ts namedExport.getAliasNode(); // returns: Identifier | undefined namedExport.setAlias("NewAliasName"); namedExport.renameAlias("NewAliasName"); ``` _Note:_ Renaming the alias will set or rename any uses of the alias or identifier to the new value. ##### Local Target Declarations The local target declarations are the declarations that the export specifier is referencing: ```ts const declarations = namedExport.getLocalTargetDeclarations(); // returns: Node[] ``` ##### Parent export declaration ```ts namedExport.getExportDeclaration(); // returns: ExportDeclaration ``` ## Export Assignments Export assignments look like the following: ```ts setup: let name: string; export = 5; export default name; ``` Get the export assignments by calling: ```ts const exportAssignments = sourceFile.getExportAssignments(); // or to get the first one that matches a condition const exportAssignment = sourceFile.getExportAssignment(d => d.isExportEquals()); // get if it's `export =` or `export default` const isExportEquals = exportAssignment.isExportEquals(); // get the expression const expression = exportAssignment.getExpression(); ``` Set whether one is an export equals or export default: ```ts exportAssignment.setIsExportEquals(false); // sets to export default ``` Set its expression: ```ts exportAssignment.setExpression(writer => writer.write("5")); exportAssignment.setExpression("6"); ``` ### Add/Insert Add or insert use `insertExportAssignment`, `insertExportAssignments`, `addExportAssignment`, or `addExportAssignments`: ```ts const exportAssignment = sourceFile.addExportAssignment({ isExportEquals: true, // defaults to true expression: "5", }); ``` ### Remove Call `.remove()`: ```ts exportAssignment.remove(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/expressions.md # Path: docs/details/expressions.md --- title: Expressions --- ## Expression With Type Arguments These are found in certain areas. For example `extends` and `implements` expressions. ### Getting expression ```ts const expression = expressionWithTypeArgs.getExpression(); // returns: Node ``` ### Getting type arguments ```ts const typeArgs = expressionWithTypeArgs.getTypeArguments(); // returns: TypeNode[] ``` ## Call Expressions Call expressions are statements that call a function: ```ts setup: const doSomething: () => void; doSomething(); ``` ### Getting call signatures From a given node, get all the children or descendants that are call expressions. For example: ```ts const childCallExpressions = node.getChildrenOfKind(SyntaxKind.CallExpression); const descendantCallExpressions = node.getDescendantsOfKind(SyntaxKind.CallExpression); ``` ### Return type Use the following: ```ts const returnType = callExpression.getReturnType(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/functions.md # Path: docs/details/functions.md --- title: Functions --- ## Function Declarations Functions can be retrieved from source files, other namespaces, or function bodies: ```ts const functions = sourceFile.getFunctions(); const function1 = sourceFile.getFunction("Function1"); const firstFunctionWithChildFunction = sourceFile.getFunction(f => f.getFunctions().length > 0); ``` Most of the information you can get about functions is covered in other sections. ### Name It's important to note that function declarations may not have a name. For example: ```ts export default function() { // etc... } ``` For this reason, the methods like `.getName()` and `.getNameNode()` are nullable on `FunctionDeclaration`. ### Add/Insert Add or insert functions to a source file, namespace, or function like declarations by calling `addFunction()`, `addFunctions()`, `insertFunction()`, or `insertFunctions()`. ```ts const functionDeclaration = sourceFile.addFunction({ name: "FunctionName", }); ``` ### Remove Call `.remove()`: ```ts functionDeclaration.remove(); ``` ### Overloads By default, in ambient contexts or for ambient nodes, all overloads will be returned. In non-ambient contexts, only the implementation will be returned. Get the overloads by calling: ```ts const overloads = functionDeclaration.getOverloads(); // returns: FunctionDeclaration[] ``` Or tell if the current declaration is an overload by calling either: ```ts functionDeclaration.isOverload(); functionDeclaration.isImplementation(); ``` From the overloads, get the implementation by calling: ```ts const implementation = functionOverload.getImplementation(); ``` #### Add/Insert Add or insert overloads by using either the `.addOverload()`, `.addOverloads()`, `.insertOverload()`, or `insertOverloads()` methods. #### Remove Call `.remove()` on the overload: ```ts functionOverload.remove(); ``` ### Set body text The body text can be set via the `.setBodyText()` method: ```ts functionDeclaration.setBodyText("const myNumber = 5;"); ``` Or alternatively, write the body text with [code-block-writer](https://github.com/dsherret/code-block-writer): ```ts functionDeclaration.setBodyText(writer => writer.writeLine("const myNumber = 5;") .write("if (myNumber === 5)").block(() => { writer.writeLine("console.log('yes')"); }) ); ``` Using the writer is very useful because it will write code out using the indentation and newline settings of the AST. It's also easier to use. ### Get body text This returns the body text without leading indentation or leading and trailing whitespace. ```ts console.log(functionDeclaration.getBodyText()); ``` ### Unwrap A function declaration can be replaced with its body using the `.unwrap()` method. Given the following code: ```ts function someFunction() { function innerFunction() { } const someDeclaration = 5; } ``` Calling `.unwrap()` on the function will change the code to the following: ```ts function innerFunction() { } const someDeclaration = 5; ``` ## Function Expressions They exist in an expression: ```ts const add = function(a: number, b: number) { return a + b; }; ``` In this case, it can be retrieved via the variable declaration's [initializer](initializers). ```ts const functionExpression = sourceFile.getVariableDeclarationOrThrow("add") .getInitializerIfKindOrThrow(SyntaxKind.FunctionExpression); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/generators.md # Path: docs/details/generators.md --- title: Generators --- ## Generators Nodes like `FunctionDeclaration` and `MethodDeclaration` can be generators. ### Tell if a generator ```ts functionDeclaration.isGenerator(); // returns: boolean ``` ### Set as a generator ```ts functionDeclaration.setIsGenerator(true); // or false to set as not one ``` ### Get asterisk token (`*`) Gets the asterisk token or undefined if not exists: ```ts functionDeclaration.getAsteriskToken(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/identifiers.md # Path: docs/details/identifiers.md --- title: Identifiers --- ## Identifiers Identifiers are nodes that reference or define the name of a node. For example, in the following code the identifiers are the variable name and the use of the variable: ```ts const identifier = 5; console.log(identifier); ``` ### Getting identifiers A lot of the time, a node will have a name and you can retrieve the identifier via `.getNameNode()`. If not, from a given node, you can get all the children or descendants that are identifiers. For example: ```ts const childIdentifiers = node.getChildrenOfKind(SyntaxKind.Identifier); const descendantIdentifiers = node.getDescendantsOfKind(SyntaxKind.Identifier); ``` ### Text Get the text: ```ts const text = identifier.getText(); ``` ### Rename Rename an identifier: ```ts identifier.rename("someNewName"); ``` ### Find References Find all the references: ```ts const references = identifier.findReferences(); ``` ### Get Definitions Gets the definitions of the identifier. This is similar to "go to definition" functionality that exists with TypeScript in most IDEs. ```ts const definitions = identifier.getDefinitions(); ``` Or just get the nodes: ```ts const nodes = identifier.getDefinitionNodes(); ``` ### Get Implementations Gets the implementations of the identifier. This is similar to "go to implementation" functionality that exists with TypeScript in most IDEs. ```ts const implementations = identifier.getImplementations(); ``` ### Type Get the type of an identifier: ```ts const identifierType = identifier.getType(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/imports.md # Path: docs/details/imports.md --- title: Imports --- ## Imports Imports of a source file or module can be retrieved by calling: ```ts // get them all const imports = sourceFile.getImportDeclarations(); // or get the first one that matches a condition const importWithDefaultImport = sourceFile.getImportDeclaration(i => i.getDefaultImport() != null); const someModuleImport = sourceFile.getImportDeclaration("module-specifier-text"); ``` ### Add/Insert Add or insert use `insertImportDeclaration`, `insertImportDeclarations`, `addImportDeclaration`, or `addImportDeclarations`: ```ts const importDeclaration = sourceFile.addImportDeclaration({ defaultImport: "MyClass", moduleSpecifier: "./file", }); ``` ### Remove Call `.remove()`: ```ts importDeclaration.remove(); ``` ### Module specifier Get it: ```ts const moduleSpecifier = importDeclaration.getModuleSpecifier(); // returns: StringLiteral ``` Or get it's value: ```ts const moduleSpecifierValue = importDeclaration.getModuleSpecifierValue(); // returns: string ``` _Example:_ For `import settings from "./settings";` would return `./settings`. Set it: ```ts importDeclaration.setModuleSpecifier("./new-file"); // or set by source file importDeclaration.setModuleSpecifier(sourceFile); ``` Get the referenced source file: ```ts const sourceFile = importDeclaration.getModuleSpecifierSourceFile(); // returns: SourceFile | undefined ``` Get if the module specifier is relative (starts with `./` or `../`): ```ts importDeclaration.isModuleSpecifierRelative(); ``` ### Default import Get it: ```ts const defaultImport = importDeclaration.getDefaultImport(); // returns: Identifier | undefined ``` Set it: ```ts importDeclaration.setDefaultImport("MyClass"); importDeclaration.renameDefaultImport("MyClass2"); importDeclaration.removeDefaultImport(); ``` #### Example Given the file: ```ts import MyClass from "./file"; const instance = new MyClass(); ``` Doing the following: ```ts const importDeclaration = sourceFile.getImportDeclarations()[0]; importDeclaration.renameDefaultImport("NewName"); ``` Will rename the default import and all its usages: ```ts import NewName from "./file"; const instance = new NewName(); ``` ### Namespace import Get it: ```ts const namespaceImport = importDeclaration.getNamespaceImport(); // returns: Identifier | undefined ``` Set it: ```ts importDeclaration.setNamespaceImport("newName"); ``` _Note:_ Setting the namespace import for an existing namespace import will rename any uses of the namespace import in the current file. Remove it: ```ts importDeclaration.removeNamespaceImport(); ``` ### Named imports Getting a named import: ```ts const namedImports = importDeclaration.getNamedImports(); // returns: ImportSpecifier ``` Adding or inserting named imports can be done via the `addNamedImport`, `addNamedImports`, `insertNamedImport`, or `insertNamedImports` methods. ```ts const namedImport = importDeclaration.addNamedImport({ name: "MyClass", alias: "MyAliasName", // alias is optional }); // or importDeclaration.addNamedImports(["MyClass", "SomeInterface"]); ``` Removing one named import: ```ts namedImport.remove(); ``` Removing all named imports: ```ts importDeclaration.removeNamedImports(); ``` #### Import specifier Import specifiers are the individual named imports. ##### Name ```ts namedImport.getNameNode(); // returns: Identifier namedImport.setName("NewName"); ``` ##### Alias ```ts namedImport.getAliasNode(); // returns: Identifier | undefined namedImport.setAlias("NewAliasName"); namedImport.renameAlias("NewAliasName"); ``` _Note:_ Renaming the alias will set or rename any uses of the alias or identifier in the current file to the new value. ##### Parent import declaration ```ts namedImport.getImportDeclaration(); // returns: ImportDeclaration ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/index.md # Path: docs/details/index.md --- title: Details --- ## Details There is navigation support for most parts of the language at the current moment, but not everything. Manipulation support is lacking. Find out more information about: - [Source Files](source-files) - [Classes](classes) - [Decorators](decorators) - [Enums](enums) - [Functions](functions) - [Imports](imports) - [Interfaces](interfaces) - [Modules](modules) - [Parameters](parameters) - [Type Parameters](type-parameters) - [Type Aliases](type-aliases) - [Variables](variables) - Type parameters - todo - Symbols - todo Types: - [Types](types) - [Signatures](signatures) - Type Nodes - todo Common: - [Expressions](expressions) - [Object Literal Expressions](object-literal-expressions) - [Identifiers](identifiers) - [Ambient](ambient) - [Async](async) - [Exports](exports) - [Generators](generators) - [Initializers](initializers) - [JS Docs](documentation) - [Literals](literals) - [Modifiers](modifiers) - [Comments](comments) - Question tokenable - todo - Readonly - todo - Return Types - todo - Scope - todo - Static - todo --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/initializers.md # Path: docs/details/initializers.md --- title: Initializers --- ## Initializers ### Getting For example, given the following code: ```ts const add = function(a: number, b: number) { return a + b; }; ``` The initializer can be retrieved in any of these ways: ```ts variableDeclaration.getInitializer(); // returns: Expression | undefined variableDeclaration.getInitializerOrThrow(); // returns: Expression variableDeclaration.getInitializerIfKind(SyntaxKind.FunctionExpression); // returns: Expression | undefined variableDeclaration.getInitializerIfKindOrThrow(SyntaxKind.FunctionExpression); // returns: Expression ``` ### Removing Use `.removeInitializer()` on the parent node. For example: ```ts variableDeclaration.removeInitializer(); ``` ### Setting Use `.setInitializer(...)`: ```ts variableDeclaration.setInitializer("2 + 2"); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/interfaces.md # Path: docs/details/interfaces.md --- title: Interfaces --- ## Interface Declarations Interface declarations can be retrieved from source files, namespaces, or function bodies: ```ts const interfaces = sourceFile.getInterfaces(); const interface1 = sourceFile.getInterface("Interface1"); const firstInterfaceWith5Properties = sourceFile.getInterface(i => i.getProperties().length === 5); ``` ### Add/Insert Add or insert interfaces to a source file, namespace, or function like declarations by calling `addInterface()`, `addInterfaces()`, `insertInterface()`, or `insertInterfaces()`. ```ts const interfaceDeclaration = sourceFile.addInterface({ name: "InterfaceName", }); ``` ### Remove Call `.remove()`: ```ts interfaceDeclaration.remove(); ``` ### Extends expressions Will return [`ExpressionWithTypeArguments[]`](expressions): ```ts const extendsExpressions = interfaceDeclaration.getExtends(); ``` Add or insert extends expressions: ```ts interfaceDeclaration.addExtends("Named"); interfaceDeclaration.addExtends(["Named", "Aged"]); interfaceDeclaration.insertExtends(1, "Named"); interfaceDeclaration.insertExtends(2, ["Named", "Aged"]); ``` Remove an expression: ```ts interfaceDeclaration.removeExtends(0); // index interfaceDeclaration.removeExtends(interfaceDeclaration.getExtends()[0]); // node ``` ### Members Get all the members of the interface: ```ts const members = interfaceDeclaration.getMembers(); ``` ### Construct signatures Use: ```ts const constructSignatures = interfaceDeclaration.getConstructSignatures(); const constructSignature = interfaceDeclaration.getConstructSignature(c => c.getParameters().length > 2); ``` #### Add/Insert To add or insert use `addConstructSignature()`, `addConstructSignatures()`, `insertConstructSignature`, or `insertConstructSignatures()`: ```ts const constructSignature = interfaceDeclaration.addConstructSignature({ returnType: "SomeClass" }); ``` #### Remove Remove a construct signature: ```ts constructSignature.remove(); ``` ### Call signatures Use: ```ts const callSignatures = interfaceDeclaration.getCallSignatures(); const callSignature = interfaceDeclaration.getCallSignature(c => c.getParameters().length > 2); ``` #### Add/Insert To add or insert use `addCallSignature()`, `addCallSignatures()`, `insertCallSignature`, or `insertCallSignatures()`: ```ts const callSignature = interfaceDeclaration.addCallSignature({ returnType: "SomeClass" }); ``` #### Remove Remove a call signature: ```ts callSignature.remove(); ``` ### Index signatures Use: ```ts const indexSignatures = interfaceDeclaration.getIndexSignatures(); const indexSignature = interfaceDeclaration.getIndexSignature(s => s.getKeyName() === "keyName"); ``` #### Add/Insert To add or insert use `addIndexSignature()`, `addIndexSignatures()`, `insertIndexSignature`, or `insertIndexSignatures()`: ```ts const indexSignature = interfaceDeclaration.addIndexSignature({ keyName: "someKey", // defaults to key keyType: "string", // defaults to string returnType: "SomeClass", }); ``` #### Remove Remove an index signature: ```ts indexSignature.remove(); ``` ### Method signatures Use: ```ts const methodSignatures = interfaceDeclaration.getMethods(); const myMethod = interfaceDeclaration.getMethod("myMethod"); const firstMethodWith4Params = interfaceDeclaration.getMethod(m => m.getParameters().length === 4); ``` #### Add/Insert To add or insert use `addMethod()`, `addMethods()`, `insertMethod`, or `insertMethods()`: ```ts const methodSignature = interfaceDeclaration.insertMethod(1, { name: "newMethod", returnType: "boolean" }); ``` #### Remove Remove a method signature: ```ts methodSignature.remove(); ``` ### Properties Use: ```ts const properties = interfaceDeclaration.getProperties(); const myProperty = interfaceDeclaration.getProperty("myProperty"); const firstStringProperty = interfaceDeclaration.getProperty(p => p.getType().getText() === "string"); ``` #### Add/Insert To add or insert use `addProperty()`, `addProperties()`, `insertProperty`, or `insertProperties()`: ```ts const propertySignature = interfaceDeclaration.insertProperty(1, { name: "newProperty", type: "string" }); ``` #### Remove Remove a property signature: ```ts propertySignature.remove(); ``` ### Base Types ```ts const baseTypes = interfaceDeclaration.getBaseTypes(); ``` ### Base Declarations Gets the base interface, type alias, or class declarations: ```ts const baseDeclarations = interfaceDeclaration.getBaseDeclarations(); ``` ### Implementations Get the implementations. ```ts const implementations = interfaceDeclaration.getImplementations(); ``` Similar to "go to implementation" command available in IDEs. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/literals.md # Path: docs/details/literals.md --- title: Literals --- ## Literals Literals: - `StringLiteral` (ex. `"some string"`) - `NumericLiteral` (ex. `5`, `10.53`) - `TrueLiteral` / `FalseLiteral` (ex. `true` / `false`) - `NoSubstitutionTemplateLiteral` (ex. `` `some string` ``) - `RegularExpressionLiteral` (ex. `/pattern/gi`) Methods: - `.getLiteralValue()` - Returns the string, number, boolean, or RegExp value. - `.setLiteralValue(...)` - Allows setting the literal value. - `isTerminated()` - If the literal is terminated. - `hasExtendedUnicodeEscape()` - If the literal has a unicode escape (ex. `\u{20bb7}`) --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/modifiers.md # Path: docs/details/modifiers.md --- title: Modifiers --- ## Modifiers Modifiers are nodes that modify other nodes. For example, the `private` keyword is a modifier that changes the scope of a method on a class. Only certain nodes can have modifiers and they will have these functions. ### Getting all modifiers ```ts functionDeclaration.getModifiers(); ``` ### Getting first modifier by syntax kind Use `getFirstModifierByKind(syntaxKind: SyntaxKind);`. ```ts functionDeclaration.getFirstModifierByKind(SyntaxKind.AsyncKeyword); ``` ### Telling if has a modifier ```ts functionDeclaration.hasModifier(SyntaxKind.AsyncKeyword); // returns: boolean ``` ### Toggle modifier Toggles a modifier on or off: ```ts functionDeclaration.toggleModifier("async"); functionDeclaration.toggleModifier("async", false); // or explicit toggle ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/modules.md # Path: docs/details/modules.md --- title: Module / Namespace Declarations --- ## Module / Namespace Declarations Module declarations may look like any of the following: ```ts ignore-error: 2664 namespace MyNamespace { } module MyModule { } declare module "some-module" { } declare module "other-module"; ``` They can be retrieved from source files or other modules/namespaces: ```ts const modules = sourceFile.getModules(); const namespace1 = sourceFile.getModule("Namespace1"); const firstNamespaceWithClass = sourceFile.getModule(n => n.getClasses().length > 0); ``` Most of the information you can get about namespaces is covered in other sections. Note: Although it's a compile error, you can also retrieve namespaces from function bodies. ### Add/Insert Add or insert namespaces to a source file, namespace, or function like declarations by calling `addModule()`, `addModules()`, `insertModule()`, or `insertModules()`. ```ts const moduleDeclaration = sourceFile.addModule({ name: "ModuleName", }); ``` ### Remove Call `.remove()`: ```ts moduleDeclaration.remove(); ``` ### Module, namespace, or `global`? Check for the declaration kind or keywords: ```ts moduleDeclaration.getDeclarationKind(); // returns: ModuleDeclarationKind // or moduleDeclaration.hasModuleKeyword(); // returns: boolean moduleDeclaration.hasNamespaceKeyword(); // returns: boolean ``` Or set the declaration kind: ```ts moduleDeclaration.setDeclarationKind(ModuleDeclarationKind.Namespace); moduleDeclaration.setDeclarationKind(ModuleDeclarationKind.Module); moduleDeclaration.setDeclarationKind(ModuleDeclarationKind.Global); ``` Or get the keyword: ```ts // returns: the module or namespace keyword or undefined if global moduleDeclaration.getDeclarationKindKeyword(); ``` **Reminder:** Module declarations that are `global` have the following syntax: ```ts ignore-error: 2664, 2669 declare module "my-library" { // this is a global namespace declaration global { const foo: string; } } ``` ### Unwrap A module declaration can be replaced with its body using the `.unwrap()` method. Given the following code: ```ts namespace MyNamespace { function someFunction() { } class SomeClass { } } ``` Calling `.unwrap()` will change the code to the following: ```ts function someFunction() { } class SomeClass { } ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/namespaces.md # Path: docs/details/namespaces.md --- title: Namespaces --- ## Namespaces See [Modules](modules). --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/object-literal-expressions.md # Path: docs/details/object-literal-expressions.md --- title: Object Literal Expressions --- ## Object Literal Expressions Object literal expressions look like the [initializer](initializers) for this variable declaration: ```ts const obj = { propertyAssignment: 5, shorthandPropertyAssignment, ...spreadAssignment, get getAccessor() { return 5; }, set setAccessor(value: number) { // do something }, method() { return "some string"; }, }; ``` ### Properties Once you have retrieved the object literal expression, get it's properties via: ```ts const properties = objectLiteralExpression.getProperties(); // or const property = objectLiteralExpression.getProperty("propertyAssignment"); // or const spreadAssignment = objectLiteralExpression.getProperty( p => p.getText() === "...spreadAssignment", ); // or const method = objectLiteralExpression.getPropertyOrThrow("method"); ``` ### Property Assignments Add a property assignment via the `insertPropertyAssignment`, `insertPropertyAssignments`, `addPropertyAssignment`, or `addPropertyAssignments` methods. ```ts const propertyAssignment = objectLiteralExpression.addPropertyAssignment({ name: "propertyAssignment", initializer: "5", }); ``` ### Shorthand Property Assignments Add a shorthand property assignment via the `insertShorthandPropertyAssignment`, `insertShorthandPropertyAssignments`, `addShorthandPropertyAssignment`, or `addShorthandPropertyAssignments` methods. ```ts const shorthandPropertyAssignment = objectLiteralExpression.addShorthandPropertyAssignment({ name: "shorthandPropertyAssignment", }); ``` ### Spread Assignments Add a spread assignment via the `insertSpreadAssignment`, `insertSpreadAssignments`, `addSpreadAssignment`, or `addSpreadAssignments` methods. ```ts const spreadAssignment = objectLiteralExpression.addSpreadAssignment({ expression: "spreadAssignment" }); ``` ### Accessors Add get and set accessors via the `insertGetAccessor`, `insertGetAccessors`, `addGetAccessor`, `addGetAccessors`, or similarly named methods for set accessors. ```ts const getAccessor = objectLiteralExpression.addGetAccessor({ name: "someNumber", returnType: "number", statements: ["return someNumber;"], }); const setAccessor = objectLiteralExpression.addSetAccessor({ name: "someNumber", parameters: [{ name: "value", type: "number" }], statements: ["someNumber = value;"], }); ``` ### Methods Add a method via the `insertMethod`, `insertMethods`, `addMethod`, or `addMethods` methods. ```ts const method = objectLiteralExpression.addMethod({ name: "method", statements: [`return "some string";`], }); ``` ### Removing Remove a member by calling `.remove()` on it. ```ts setup: const obj: ObjectLiteralExpression; obj.getPropertyOrThrow("prop1").remove(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/parameters.md # Path: docs/details/parameters.md --- title: Parameters --- ## Parameters Parameters can be retreived from nodes by calling `getParameters()`: ```ts const parameters = functionDeclaration.getParameters(); ``` ### Inserting/Adding Insert or add parameters by calling `insertParameter()`, `insertParameters()`, `addParameter()`, or `addParameters()`. For example: ```ts const parameter = functionDeclaration.insertParameter(1, { name: "param1", type: "string", }); ``` ### Removing Remove a parameter by calling `.remove()` on it: ```ts parameter.remove(); ``` ### Rest parameter Tell if a parameter is a rest parameter: ```ts const isRestParameter = parameter.isRestParameter(); // returns: boolean ``` Or set a parameter as a rest parameter: ```ts parameter.setIsRestParameter(true); ``` ### Parameter Property Constructor parameters may be properties when they have a scope and/or are readonly. You can tell if one is by calling: ```ts const isParameterProperty = parameter.isParameterProperty(); // returns: boolean ``` ### Optional A parameter can be optional if it is marked so with a question mark, is a rest parameter, or has an initializer. `isOptional()` can be used to tell if any of these are true: ```ts const isOptional = parameter.isOptional(); // returns: boolean ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/signatures.md # Path: docs/details/signatures.md --- title: Signatures --- ## Signatures ### Type Parameters ```ts const typeParams = signature.getTypeParameters(); // returns: TypeParameter[] ``` ### Parameters ```ts const params = signature.getParameters(); // returns: Symbol[] ``` ### Return Type ```ts const returnType = signature.getReturnType(); ``` ### Documentation Comments ```ts const docs = signature.getDocumentationComments(); ``` ### JS Doc Tags ```ts const tags = signature.getJsDocTags(); ``` ### Declaration ```ts const declaration = signature.getDeclaration(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/source-files.md # Path: docs/details/source-files.md --- title: Source Files --- ## Source Files Source files are the root nodes of the AST. They can be created or added to a `Project` instance by following the instructions at [Adding Source Files](../setup/adding-source-files). ### File path and name Use: ```ts // returns the file path (ex. /home/david/file.ts) sourceFile.getFilePath(); // returns only the file name (ex. file.ts) sourceFile.getBaseName(); ``` ### Other Information Check if it's a declaration file: ```ts sourceFile.isDeclarationFile(); // returns: boolean ``` Check if it was discovered from an external module: ```ts sourceFile.isFromExternalLibrary(); ``` Check if it's a descendant of a `node_modules` directory: ```ts sourceFile.isInNodeModules(); ``` ### Save Save a source file to the file system using one of the following commands: ```ts await sourceFile.save(); sourceFile.saveSync(); ``` Note: This does not emit the files (see [Emitting](../emitting.md)). ### Unsaved files Use the `sourceFile.isSaved()` method that will tell you if the file is saved to the file system. ### Delete The `sourceFile.delete()` method will queue up deletions to the file system. When done call `project.save()`. For example: ```ts import { Project } from "ts-morph"; // queue up all the source files to be deleted const project = new Project(); project.addSourceFilesAtPaths("folder/**/*.ts"); project.getSourceFileOrThrow("someFile.ts").delete(); project.getSourceFileOrThrow("someOtherFile.ts").delete(); // after you're all done, finally save your changes to the file system await project.save(); ``` #### Deleting Immediately It's possible to delete a source file from the file system immediately by calling one of the following methods: ```ts await sourceFile.deleteImmediately(); sourceFile.deleteImmediatelySync(); ``` This isn't recommended though because it could possibly leave the file system in a halfway state if your code errors before it's done. ### Copy Copy a source file to a new file by specifying a new relative or absolute path: ```ts setup: const someDirectoryObject: Directory const newSourceFile = sourceFile.copy("newFileName.ts"); // this won't throw if a file exists at the specified path const otherSourceFile = sourceFile.copy("other.ts", { overwrite: true }); // or specify directory to copy to sourceFile.copyToDirectory("/some/dir"); sourceFile.copyToDirectory(someDirectoryObject); ``` Note: If necessary, this will automatically update the module specifiers of the relative import and export declarations in the copied file. #### Copying Immediately As with `.deleteImmediately()` it's possible to immediately copy a file and have the changes reflected on the file system: ```ts await sourceFile.copyImmediately("NewFile.ts"); sourceFile.copyImmediatelySync("NewFile2.ts"); ``` ### Move Moves a source file to a new file by specifying a new relative or absolute path: ```ts setup: const someDirectoryObject: Directory sourceFile.move("newFileName.ts"); // this won't throw if a file exists at the specified path sourceFile.move("other.ts", { overwrite: true }); // or specify directory to move to sourceFile.moveToDirectory("/some/dir"); sourceFile.moveToDirectory(someDirectoryObject); ``` Note: If necessary, this will automatically update the module specifiers of the relative import and export declarations in the moving file and the relative import and export declarations in other files to point to the new location. #### Moving Immediately As with `.deleteImmediately()` it's possible to immediately move a file and have the changes reflected on the file system: ```ts await sourceFile.moveImmediately("NewFile.ts"); sourceFile.moveImmediatelySync("NewFile2.ts"); ``` ### Refresh from file system Refresh the source file from the file system: ```ts import { FileSystemRefreshResult } from "ts-morph"; // returns: FileSystemRefreshResult (NoChange, Updated, Deleted) const result = await sourceFile.refreshFromFileSystem(); // or refreshFromFileSystemSync() ``` This is useful when you are using a file system watcher and want to refresh a source file from the file system based on changes. If the file was _updated_: All the child nodes of the source file will be forgotten and you will have to renavigate the file. If the file was _deleted_: The source file will be removed and all its nodes forgotten. ### Remove Remove a source file from the project by calling: ```ts sourceFile.forget(); ``` Or alternatively: ```ts project.removeSourceFile(sourceFile); // returns: boolean (true if was removed) ``` Note: This does not delete the file from the file system. To do delete it, call `#delete()`. ### Reference comments ```ts // gets `/// ` comments const pathReferenceDirectives = sourceFile.getPathReferenceDirectives(); // gets `/// ` comments const typeReferenceDirectives = sourceFile.getTypeReferenceDirectives(); // gets `/// ` comments const libReferenceDirectives = sourceFile.getLibReferenceDirectives(); ``` ### Import Declarations See [Import Declarations](imports). ### Export Declarations See [Export Declarations](exports). ### Indenting / Unindenting Call the `.indent` or `.unindent` methods. ```ts sourceFile.indent(5); // indent line containing position 5 sourceFile.indent([5, 10]); // indent line or lines within position range [5-10] sourceFile.indent(10, 3); // indent line containing position 10, 3 times sourceFile.unindent(10); // unindent line containing position 10 sourceFile.indent(10, -1); // unindent line containing position 10 (specify negative times) sourceFile.unindent(10, -1); // indent line containing position 10 (specify negative times) ``` This will indent and unindent based on your [manipulation settings](../manipulation/settings). ### Getting referencing files Getting the source files that reference this source file in nodes like import declarations, export declarations, import equals declarations, and dynamic imports can be found by using the following: ```ts const referencingSourceFiles = sourceFile.getReferencingSourceFiles(); ``` To get the nodes that reference the source file in other source files: ```ts const referencingNodes = sourceFile.getReferencingNodesInOtherSourceFiles(); ``` To get the string literals that reference this source file in other source files: ```ts const referencingLiterals = sourceFile.getReferencingLiteralsInOtherSourceFiles(); ``` ### Getting referenced files The opposite of the referencing files, is the referenced files—files that are referenced in nodes within the current file. ```ts const referencedSourceFiles = sourceFile.getReferencedSourceFiles(); ``` To get the nodes that reference other source files: ```ts const nodesReferencingOtherSourceFiles = sourceFile.getNodesReferencingOtherSourceFiles(); ``` To get the string literals that reference other source files: ```ts const literalsReferencingOtherSourceFiles = sourceFile.getLiteralsReferencingOtherSourceFiles(); // or to get all the literals that reference a module (and may not have been resolved to a source file) const importLiterals = sourceFile.getImportStringLiterals(); ``` ### Relative File Paths It might be useful to get the relative path from one source file to another source file or directory. ```ts setup: let sourceFileFrom: SourceFile, sourceFileTo: SourceFile; const relativePath = sourceFileFrom.getRelativePathTo(sourceFileTo); ``` Or to get the module specifier text from one source file to another source file or directory. ```ts setup: let sourceFileFrom: SourceFile, sourceFileTo: SourceFile; const moduleSpecifier = sourceFileFrom.getRelativePathAsModuleSpecifierTo(sourceFileTo); ``` ## Source File Code Fixes ### Organizing Imports It's possible to organize all the imports in a source file via the ["organize imports"](https://blogs.msdn.microsoft.com/typescript/2018/03/27/announcing-typescript-2-8/) feature from TypeScript 2.8. ```ts sourceFile.organizeImports(); ``` Note: This will forget all the previously navigated nodes so it's recommended to make this either the first or last action you do to a source file. ### Add Missing Imports To add all the missing import declarations to a source file, use the following method: ```ts sourceFile.fixMissingImports(); ``` ### Fix Unused Identifiers To remove all unused declarations (ex. variables, classes, etc..) from a source file, use the following method: ```ts sourceFile.fixUnusedIdentifiers(); ``` Tip: For optimal results, sometimes this method needs to be called more than once. There could be nodes that are only referenced in unused declarations and in this case another call would remove them. Note: This will forget all the previously navigated nodes so it's recommended to make this either the first or last action you do to a source file. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/type-aliases.md # Path: docs/details/type-aliases.md --- title: Type Aliases --- ## Type Aliases Type aliases can be retrieved from source files, namespaces, or function bodies: ```ts const typeAliases = sourceFile.getTypeAliases(); const typeAlias = sourceFile.getTypeAlias("TypeAlias"); const firstExportedTypeAlias = sourceFile.getTypeAlias(a => a.hasExportKeyword()); ``` Most of the information you can get about type aliases is covered in other sections. ### Add/Insert Add or insert type aliases to a source file, namespace, or function like declarations by calling `addTypeAlias()`, `addTypeAliases()`, `insertTypeAlias()`, or `insertTypeAliases()`. ```ts const typeAliasDeclaration = sourceFile.addTypeAlias({ name: "TypeAliasName", type: "string", }); ``` ### Remove Call `.remove()`: ```ts typeAliasDeclaration.remove(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/type-parameters.md # Path: docs/details/type-parameters.md --- title: Type Parameters --- ## Type Parameters Type parameters can be retreived from nodes by calling `getTypeParameters()`: ```ts const typeParameters = classDeclaration.getTypeParameters(); ``` ### Inserting/Adding Insert or add type parameters by calling `insertTypeParameter()`, `insertTypeParameters()`, `addTypeParameter()`, or `addTypeParameters()`. For example: ```ts const typeParameter = classDeclaration.insertTypeParameter(1, { name: "T", constraint: "string", // optional }); ``` ### Removing Remove a type parameter by calling `.remove()` on it: ```ts typeParameter.remove(); ``` ### Constraint Get the constraint type node: ```ts const constraint = typeParameter.getConstraint(); // returns: TypeNode | undefined ``` Or set the constraint: ```ts typeParameter.setConstraint("string"); ``` Or remove it: ```ts typeParameter.removeConstraint(); ``` ### Default Get the default type node: ```ts const defaultNode = typeParameter.getDefault(); // returns: TypeNode | undefined ``` Or set the default type node: ```ts typeParameter.setDefault("string"); ``` Or remove it: ```ts typeParameter.removeDefault(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/types.md # Path: docs/details/types.md --- title: Types --- ## Types Types are accessed by calling `.getType()` on nodes that are typed. For example: ```ts const type = parameter.getType(); ``` There are other ways for accessing a type. For example: ```ts const returnType = functionDeclaration.getReturnType(); ``` ### Checking type is assignable to another type Note: Requires ts-morph 22+ To check if a type is assignable to another type, use the `isAssignableTo` method: ```ts if (stringLitType.isAssignableTo(stringType)) { // ... } ``` ### Compiler Type The underlying compiler type can be accessed via: ```ts const compilerType = type.compilerType; ``` ### Apparent type Given the following variable declaration: ```ts const myVar = 4; ``` The type is `4` and the apparent type is `Number`. Retrieve the apparent type via the following: ```ts const apparentType = type.getApparentType(); ``` ### Text Getting the type text can be achieved by calling `.getText()`: ```ts const text = type.getText(); ``` Sometimes this may not be good enough. If not, try to provide the enclosing node: ```ts const text = type.getText(parameter); ``` Format it by providing `TypeFormatFlags`: ```ts const text = type.getText(parameter, TypeFormatFlags.NoTruncation | TypeFormatFlags.WriteArrayAsGenericType); ``` Look at the declaration file for more available options for `TypeFormatFlags`. ### Constraint and Default ```ts const constraintType = type.getConstraint(); const defaultType = type.getDefault(); ``` ### Intersection types ```ts const intersectionTypes = type.getIntersectionTypes(); ``` ### Union types ```ts const unionTypes = type.getUnionTypes(); ``` ### Properties Get the properties or property of a type: ```ts const properties = type.getProperties(); const prop1 = type.getProperty("prop1"); const prop2 = type.getProperty(p => p.getName() === "prop2"); ``` Or the apparent properties: ```ts const apparentProperties = type.getApparentProperties(); const prop1 = type.getApparentProperty("prop1"); const prop2 = type.getApparentProperty(p => p.getName() === "prop2"); ``` ### Base types ```ts const baseTypes = type.getBaseTypes(); ``` ### Base type of a literal type ```ts setup: let numberLiteralType: Type; const numberType = numberLiteralType.getBaseTypeOfLiteralType(); ``` ### Call signatures ```ts const callSignatures = type.getCallSignatures(); ``` ### Construct signatures Get the construct signatures (new signatures) of a type: ```ts const constructSignatures = type.getConstructSignatures(); ``` ### Index types Get either the string index type (ex. for `{ [index: string]: Date; }` it would be `Date`) or the number index type (ex. for `{ [index: number]: object; }` it would be `object`): ```ts const stringIndexType = type.getStringIndexType(); const numberIndexType = type.getNumberIndexType(); ``` ### Tuple element types ```ts const tupleElements = type.getTupleElements(); ``` For example, for the type `[string, number]`, the above would return an array containing the type for `string` and `number`. ### Non-nullable type Gets the non-nullable type from a nullable type: ```ts const nonNullableType = type.getNonNullableType(); ``` For example, `string | undefined` would return `string`. ### Type flags This has information about the type, such as `TypeFlags.BooleanLike`. ```ts const flags = type.getFlags(); ``` Generally a method that starts with "is" exists on the type and you can easily use that instead of checking the flags (same with Object flags below). ### Object flags This has information about object types, such as `ObjectFlags.Mapped`. ```ts const objectFlags = type.getObjectFlags(); ``` ### Symbol Get the symbol of the type if it exists: ```ts const typeSymbol = type.getSymbol(); ``` ### Alias symbol ```ts const aliasSymbol = type.getAliasSymbol(); ``` ### Alias type arguments ```ts const aliasTypeArgs = type.getAliasTypeArguments(); ``` ### Telling type Use any of the following methods: ```ts type.isAnonymous(); type.isAny(); type.isArray(); type.isBoolean(); type.isBooleanLiteral(); type.isClass(); type.isClassOrInterface(); type.isEnum(); type.isEnumLiteral(); type.isInterface(); type.isIntersection(); type.isLiteral(); type.isNull(); type.isNumber(); type.isNumberLiteral(); type.isObject(); type.isString(); type.isStringLiteral(); type.isTemplateLiteral(); type.isTuple(); type.isUndefined(); type.isUnion(); type.isUnionOrIntersection(); type.isUnknown(); ``` If you see something that doesn't exist here and should (there's a lot missing), then please log an issue or submit a pull request. ### Removing a Type Remove a type or a return type from a node: ```ts propertyDeclaration.removeType(); functionDeclaration.removeReturnType(); ``` ### TODO Not implemented. Getting... - Enum member types - Destructuring pattern - More...? --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/details/variables.md # Path: docs/details/variables.md --- title: Variables --- ## Variable Statement A variable statement looks like the following: ```ts export const var1 = "5", var2 = "6"; ``` ### Get a variable statement Can be retrieved from source files, namespaces, or function bodies: ```ts const variableStatements = sourceFile.getVariableStatements(); const firstExportedVariableStatement = sourceFile.getVariableStatement(s => s.hasExportKeyword()); ``` ### Add/Insert Add or insert variable statements to a source file, namespace, or function like declarations by calling `addVariableStatement()`, `addVariableStatements()`, `insertVariableStatement()`, or `insertVariableStatements()`. ```ts import { Project, VariableDeclarationKind } from "ts-morph"; const variableStatement = sourceFile.addVariableStatement({ declarationKind: VariableDeclarationKind.Const, // defaults to "let" declarations: [{ name: "myNumber", initializer: "5", }, { name: "myString", type: "string", initializer: `'my string'`, }], }); ``` ### Remove Call `.remove()`: ```ts variableStatement.remove(); ``` ### Declaration type Get: ```ts const declarationKind = variableStatement.getDeclarationKind(); ``` It will return one of the following values: ```ts import { VariableDeclarationKind } from "ts-morph"; VariableDeclarationKind.Let; VariableDeclarationKind.Const; VariableDeclarationKind.Var; ``` Set: ```ts variableStatement.setDeclarationKind(VariableDeclarationKind.Const); ``` ## Variable Declaration These are the individual declarations within a variable statement. ### Get variable declaration Get them from the variable statement: ```ts const variableDeclarations = variableStatement.getDeclarations(); ``` Or from source files, namespaces, and function bodies: ```ts const variableDeclarations = sourceFile.getVariableDeclarations(); const variableDeclaration = sourceFile.getVariableDeclaration("myVar"); const firstStringTypedVariableDeclaration = sourceFile.getVariableDeclaration(v => v.getType().getText() === "string"); ``` ### Add/Insert Add or insert variable declarations to a variable statement by calling `addDeclaration()`, `addDeclarations()`, `insertDeclaration()`, or `insertDeclarations()`. ```ts const declaration = variableStatement.addDeclaration({ name: "num", type: "number" }); ``` ### Remove Call `.remove()`: ```ts variableDeclaration.remove(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/emitting.md # Path: docs/emitting.md --- title: Emitting --- ## Emitting Emitting is is the process of taking the original TypeScript files and outputting them as JavaScript (`.js`) and/or declaration files (`.d.ts`). Here's an example: ```ts const project = new Project({ compilerOptions: { outDir: "dist", declaration: true } }); project.createSourceFile("MyFile.ts", "const num = 1;"); project.emit(); // async // or project.emitSync(); // slow ``` This outputs two files in the `dist` folder: ```ts // MyFile.js var num = 1; // MyFile.d.ts declare const num = 1; ``` ### Emitting a single file Call `.emit()` on the source file: ```ts const sourceFile = project.getSourceFileOrThrow("MyFile.ts"); sourceFile.emit(); // async, fast // or sourceFile.emitSync(); // slow ``` Or get its emit output: ```ts const emitOutput = sourceFile.getEmitOutput(); emitOutput.getEmitSkipped(); // returns: boolean for (const outputFile of emitOutput.getOutputFiles()) { outputFile.getFilePath(); outputFile.getWriteByteOrderMark(); outputFile.getText(); } ``` ### Emitting only declaration files (.d.ts) Specify the `emitOnlyDtsFiles` flag: ```ts project.emit({ emitOnlyDtsFiles: true }); ``` ### Emit Diagnostics Diagnostics about the emit can be found on the result: ```ts const emitResult = await project.emit(); for (const diagnostic of emitResult.getDiagnostics()) console.log(diagnostic.getMessageText()); ``` These are good to always check when emitting to ensure everything went smoothly. They will explain why files aren't being emitted. ### Emitting with custom transformations You can emit using the compiler API's custom transformations by specifying them on the `customTransformers` option. The following example will emit the code with all numeric literals change to string literals: ```ts project.emit({ customTransformers: { // optional transformers to evaluate before built in .js transformations before: [context => sourceFile => visitSourceFile(sourceFile, context, numericLiteralToStringLiteral)], // optional transformers to evaluate after built in .js transformations after: [], // optional transformers to evaluate after built in .d.ts transformations afterDeclarations: [], }, }); function visitSourceFile( sourceFile: ts.SourceFile, context: ts.TransformationContext, visitNode: (node: ts.Node, context: ts.TransformationContext) => ts.Node, ) { return visitNodeAndChildren(sourceFile) as ts.SourceFile; function visitNodeAndChildren(node: ts.Node): ts.Node { return ts.visitEachChild(visitNode(node, context), visitNodeAndChildren, context); } } function numericLiteralToStringLiteral(node: ts.Node, context: ts.TransformationContext) { if (ts.isNumericLiteral(node)) return context.factory.createStringLiteral(node.text); return node; } ``` ## Emitting to Memory If you don't want to emit to the file system, you can call `emitToMemory()`: ```ts const project = new Project({ compilerOptions: { outDir: "dist" } }); project.createSourceFile("MyFile.ts", "const num = 1;"); const result = project.emitToMemory(); // output the emitted files to the console for (const file of result.getFiles()) { console.log("----"); console.log(file.filePath); console.log("----"); console.log(file.text); console.log("\n"); } // or finally save this result to the underlying file system (or use `saveFilesSync()`) result.saveFiles().then(() => console.log("written")); ``` To manipulate after emitting, you may load the result into a new project and manipulate that: ```ts const project = new Project({ compilerOptions: { outDir: "dist" } }); project.createSourceFile("MyFile.ts", "const num = 1;"); const result = project.emitToMemory(); // load the javascript files into a new project const newProject = new Project(); for (const file of result.getFiles()) newProject.createSourceFile(file.filePath, file.text, { overwrite: true }); // ...manipulate the javascript files here... // save the new files to the file system await newProject.save(); ``` ...but consider using the custom transformers discussed above if you want it to be faster. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/index.md # Path: docs/index.md --- title: Documentation --- ## Purpose Setup, navigation, and manipulation of the TypeScript AST can be a challenge. This library wraps the TypeScript compiler API so it's simple. ## Installing Install as usual via npm: ``` npm install --save-dev ts-morph ``` Or if you're using Deno and want to install via JSR: ``` deno add ts-morph@jsr:@ts-morph/ts-morph ``` ## Documentation Progress I've been still slowly updating the documentation. I'm keeping it up to date with any new features, but some existing features don't have documentation. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/code-writer.md # Path: docs/manipulation/code-writer.md --- title: Code Writer --- ## Code Writer With manipulation methods that accept a `string` for the new code, it's possible to write text using a provided [code-block-writer](https://github.com/dsherret/code-block-writer). Using the writer is very useful because it will write code out using the indentation and newline settings of the project. It's also easier to use. ### Example ```ts functionDeclaration.setBodyText(writer => writer.writeLine("let myNumber = 5;") .write("if (myNumber === 5)").block(() => { writer.writeLine("console.log('yes')"); }) ); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/formatting.md # Path: docs/manipulation/formatting.md --- title: Formatting --- ## Formatting Sometimes you might encounter code that looks terrible. For example: ```ts // BadlyFormattedFile.ts var myVariable : string | number; function myFunction(param : string){ return ""; } ``` Automatically format the text of this file by calling format text on it: ```ts sourceFile.formatText(); // or provide optional formatting settings sourceFile.formatText({ placeOpenBraceOnNewLineForFunctions: true, }); ``` This will run the source file's text through the TypeScript compiler's formatting API, which will change the source file to contain the following text: ```ts // BadlyFormattedFile.ts (not anymore!) var myVariable: string | number; function myFunction(param: string) { return ""; } ``` ### Individual Nodes Individual nodes can also be formatted. For example, say you have a file that looks like this: ```ts // file.ts export class MyClass { prop ! : string ; myMethod( example: string ) { console.log( example ); } } ``` You can select down to the specific node you want to format: ```ts project.getSourceFileOrThrow("file.ts") .getClassOrThrow("MyClass") .getInstanceMethodOrThrow("myMethod") .getStatements()[0] .formatText(); ``` Which would selectively only format the first statement in the method: ```ts // file.ts export class MyClass { prop ! : string ; myMethod( example: string ) { console.log(example); } } ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/index.md # Path: docs/manipulation/index.md --- title: Manipulating Source Files --- ## Manipulating Source Files Most information about manipulation can be found in the [Details](../details) section. This section only contains general information about manipulation. ### Saving Changes All moves, copies, and deletes won't be propagated to the underlying file system until `save()` is called on the main `project` object. ```ts import { Project } from "ts-morph"; const project = new Project(); // ...lots of code here that manipulates, copies, moves, and deletes files... // when you're all done, call this and it will save everything to the file system await project.save(); ``` The above is recommended because it means if your code errors halfway through, the files won't be in a halfway state. However, there's always a way to save, move, copy, and delete while immediately having these changes happen on the underlying file system. For example: ```ts // or use the synchronous alternatives (ex. saveSync()) await sourceFile.save(); await sourceFile.deleteImmediately(); await sourceFile.copyImmediately("copiedFile.ts"); await sourceFile.moveImmediately("movedFile.ts"); await directory.save(); await directory.deleteImmediately(); await directory.copyImmediately("CopiedDir"); await directory.moveImmediately("MovedDir"); ``` ### Replacing any node with new text Use the `.replaceWithText(...)` method that exists on any node. This will replace the text from the `Node#getStart(true)` position (start position with js docs) to `Node#getEnd()`. Use `Node#getText(true)` to get all the text that will be replaced. #### Example Given the following code: ```ts setup: let Some: any; let myVariable = Some.Property.Access.Expression; ``` You can replace the property access expression with new text by doing the following: ```ts const originalInitializer = sourceFile.getVariableDeclarations()[0].getInitializerOrThrow(); const newInitializer = originalInitializer.replaceWithText("MyReference"); ``` That will make the source file hold the following text: ```ts setup: let MyReference: any; let myVariable = MyReference; ``` Note that `originalInitializer` will be forgotten after calling `.replaceWithText(...)` on it—an error will be thrown if you try to use it. You will have to use the new node returned by that method. ### Adding, inserting, and removing statements Statements can be added, inserted, or removed from nodes with a body (ex. functions, methods, namespaces, source files). ```ts // add statements const statements = sourceFile.addStatements("console.log(5);\nconsole.log(6);"); // insert statements (index is the child index to insert at) const statements = sourceFile.insertStatements(3, "console.log(5);\nconsole.log(6);"); // remove statements sourceFile.removeStatements([1, 3]); // removes statements from index 1 to 3 sourceFile.removeStatement(1); // removes statement at index 1 ``` When adding or inserting, you can also write using a [code writer](code-writer): ```ts functionDeclaration.addStatements(writer => { writer.write("if (true)").block(() => { writer.write("something;"); }); }); ``` ### Inserting, replacing, and removing any text In some scenarios, a simple to use API might not have been implemented. If you find that's the case, open an issue on GitHub. In the meantime, you can insert, replace, and remove text using the following methods, but _generally you will want to avoid using these if possible_: ```ts // insert text sourceFile.insertText(0, writer => writer.writeLine("// some comment")); // or provide a string // replace text sourceFile.replaceText([3, 7], "a"); // "// a comment\n" // remove text sourceFile.removeText(sourceFile.getPos(), sourceFile.getEnd()); ``` These methods are also available on any node that has a body (functions, classes, enums, etc.) #### **Warning** If you use `insertText`, `replaceText`, or `removeText`, all previously navigated descendants of the node will be forgotten and not be available for use—an error will be thrown if you try to use them. You will have to renavigate to those nodes. For example: ```ts let classDeclaration = sourceFile.addClass({ name: "MyClass" }); sourceFile.insertText(0, "// some comment\n"); // this will throw... classDeclaration.getInstanceProperties(); // you'll need to get the reference again: classDeclaration = sourceFile.getClass("MyClass")!; ``` ## Code Fixes There are a variety of useful code fixes and refactors such as: - `SourceFile#organizeImports()` - `SourceFile#fixMissingImports()` - `SourceFile#fixUnusedIdentifiers()` Check more details on the [source files details page](../details/source-files). --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/order.md # Path: docs/manipulation/order.md --- title: Order --- ## Order Change the order of certain nodes using the `.setOrder(newIndex: number)` method. ```ts const interfaceDeclaration = sourceFile.getInterfaceOrThrow("MyInterface"); interfaceDeclaration.setOrder(2); ``` Notice: Right now this is not supported on comma separated nodes. See [Issue #44](https://github.com/dsherret/ts-morph/issues/44) for more information. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/performance.md # Path: docs/manipulation/performance.md --- title: Performance --- ## Performance There's a lot of opportunity for performance improvements. The library originally started off favouring correctness, but it's now starting to switch to improving performance. [View Issues](https://github.com/dsherret/ts-morph/labels/performance) ### Manipulations are slow... Right now with every manipulation the following occurs: 1. The file text is updated. 2. The new text is parsed and a new AST is created using the compiler API. 3. The previously wrapped nodes are backfilled with new compiler nodes. This might not be too bad when working with small to medium sized files, but large files may take a bit of time. If you find it's too slow, then I recommend reading the performance tips below. Note: I'm working on eliminating the need to do a complete parse of the source file between manipulations. Once implemented these performance tips won't be necessary. ### Performance Tip: Work With Structures Instead Structures are simplified ASTs. You can get a huge performance improvement by working with structures as much as possible. This is especially useful to do if you are code generating. Example: ```ts // this code will get a structure from declarations.ts, make all the descendants not be exported, // then create a new file called private.ts from that structure import { forEachStructureChild, SourceFileStructure, StructureKind, Structures, Structures } from "ts-morph"; const project = new Project({ tsConfigFilePath: "tsconfig.json" }); const classesFile = project.getSourceFileOrThrow("declarations.ts"); const classesFileStructure = classesFile.getStructure(); removeExports(classesFileStructure); project.createSourceFile("private.ts", classesFileStructure); function removeExports(structure: Structures) { forEachStructureChild(structure, removeExports); if (Structure.isExportable(structure)) structure.isExported = false; } ``` Read more in [structures](structures.md). ### Performance Tip: Batch operations You can reduce the amount of parsing that needs to happen by batching operations. For example, instead of writing code like this: ```ts setup: const classStructures: ClassDeclarationStructure[]; for (const classStructure of classStructures) sourceFile.addClass(classStructure); ``` Write this instead: ```ts setup: const classStructures: ClassDeclarationStructure[]; sourceFile.addClasses(classStructures); ``` ### Performance Tip: Analyze then Manipulate If the code analysis is using types, symbols type checker, or program, then a large performance improvement can be gained by doing an initial analysis of the code first, then afterwards carrying out the manipulations. For example, given the following code: ```ts setup: const sourceFiles: SourceFile[]; const someCheckOnSymbol: any; for (const sourceFile of sourceFiles) { for (const classDec of sourceFile.getClasses()) { if (someCheckOnSymbol(classDec.getSymbolOrThrow())) classDec.remove(); } } ``` Write it this way instead: ```ts setup: const sourceFiles: SourceFile[]; const someCheckOnSymbol: any; for (const classDec of getClassesToRemove()) classDec.remove(); function getClassesToRemove() { const classesToRemove: ClassDeclaration[] = []; for (const sourceFile of sourceFiles) { for (const classDec of sourceFile.getClasses()) { if (someCheckOnSymbol(classDec.getSymbolOrThrow())) classesToRemove.push(classDec); } } return classesToRemove; } ``` This is because the program is reset between manipulations. ### Tracking Nodes - Overview This library makes manipulations easy for you by keeping track of how the underlying syntax tree changes between manipulations. Behind the scenes, when you manipulate the AST: 1. A new source file is created using the TypeScript compiler. 2. The previously navigated nodes have their underlying compiler nodes replaced with the new compiler nodes. It's why you can do this: ```ts // sourcefile contains: interface Person { name: string; } const personInterface = sourceFile.getInterfaceOrThrow("Person"); const nameProperty = personInterface.getPropertyOrThrow("name"); nameProperty.setType("number"); nameProperty.getText(); // "name: number;" ``` Instead of having to renavigate the tree after each manipulation: ```ts // thankfully the library does not work this way let personInterface = sourceFile.getInterfaceOrThrow("Person"); let nameProperty = personInterface.getPropertyOrThrow("name"); nameProperty.setType("number"); nameProperty.getText(); // "name: string;" personInterface = sourceFile.getInterfaceOrThrow("Person"); nameProperty = personInterface.getPropertyOrThrow("name"); nameProperty.getText(); // "name: number;" ``` When thinking about performance, the key point here is that if you have a lot of previously navigated nodes and a very large file, then manipulation might start to become sluggish. #### Forgetting Nodes (Advanced) The main way to improve performance when manipulating, is to "forget" a node when you're done with it. ```ts setup: let personInterface: InterfaceDeclaration; personInterface.forget(); // or to only forget a node's descendants that are currently in the wrapped cache sourceFile.forgetDescendants(); ``` That will stop tracking the node and all its previously navigated descendants (ex. in this case, `nameProperty` as well). It won't be updated when manipulation happens again. Note that after doing this, the node will throw an error if one of its properties or methods is accessed. #### Forget Blocks (Advanced) It's possible to make sure all created nodes within a block are forgotten: ```ts import { ClassDeclaration, InterfaceDeclaration, ModuleDeclaration, Project } from "ts-morph"; const project = new Project(); const text = "namespace Namespace { interface Interface {} class Class {} }"; const sourceFile = project.createSourceFile("file.ts", text); let moduleDeclaration: ModuleDeclaration; let interfaceDeclaration: InterfaceDeclaration; let classDeclaration: ClassDeclaration; project.forgetNodesCreatedInBlock(remember => { moduleDeclaration = sourceFile.getModuleOrThrow("Namespace"); interfaceDeclaration = moduleDeclaration.getInterfaceOrThrow("Interface"); classDeclaration = moduleDeclaration.getClassOrThrow("Class"); // you can mark nodes to remember outside the scope of this block... // this will remember the specified node and all its ancestors remember(interfaceDeclaration); // or pass in multiple nodes }); moduleDeclaration.getText(); // ok, child was implicitly marked to remember interfaceDeclaration.getText(); // ok, was explicitly marked to remember classDeclaration.getText(); // throws, was forgotten // alternatively, return the node to remember it const node = project.forgetNodesCreatedInBlock(() => { const classDec = sourceFile.getClassOrThrow("MyClass"); // ...do a lot of stuff... return classDec; }); node.getText(); // ok ``` Also, do not be concerned about nesting forget blocks. That is perfectly fine to do: ```ts project.forgetNodesCreatedInBlock(() => { moduleDeclaration = sourceFile.getModuleOrThrow("Namespace"); interfaceDeclaration = moduleDeclaration.getInterfaceOrThrow("Interface"); project.forgetNodesCreatedInBlock(remember => { classDeclaration = moduleDeclaration.getClassOrThrow("Class"); remember(moduleDeclaration); }); classDeclaration.getText(); // throws, was forgotten outside the block above interfaceDeclaration.getText(); // ok, hasn't been forgotten yet }); moduleDeclaration.getText(); // ok, was marked to remember in one of the blocks interfaceDeclaration.getText(); // throws, was forgotten classDeclaration.getText(); // throws, was forgotten ``` ##### Async This method supports async and await: ```ts await project.forgetNodesCreatedInBlock(async remember => { // do stuff }); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/removing.md # Path: docs/manipulation/removing.md --- title: Removing --- ## Removing Given the source file for following code: ```ts enum MyEnum { myMember, } ``` Removing can be done as follows: ```ts const member = sourceFile.getEnum("MyEnum")!.getMember("myMember")!; member.remove(); ``` So the file above would now contain the following code: ```ts enum MyEnum { } ``` ### Support Currently removing is implemented individually for each kind of node. In general this will work for many kind of nodes, including methods, properties, constructors, parmeters, statements, declarations. Nevertheless, if you find that `remove()` method is not implemented for a particular kind of Node, please open an issue on github. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/renaming.md # Path: docs/manipulation/renaming.md --- title: Renaming --- ## Renaming Given the source file for following code: ```ts enum MyEnum { myMember, } const myVar = MyEnum.myMember; ``` Renaming can be done as follows: ```ts const myEnum = sourceFile.getEnum("MyEnum")!; myEnum.rename("NewEnum"); ``` Which will rename all usages of `MyEnum` to `NewEnum` across _all_ files. So the file above would now contain the following code: ```ts enum NewEnum { myMember, } const myVar = NewEnum.myMember; ``` ### Renaming in comments and strings Set the `renameInComments` and `renameInStrings` options to `true` (they are `false` by default): ```ts setup: let myEnum: EnumDeclaration; myEnum.rename("SomeOtherName", { renameInComments: true, renameInStrings: true, }); ``` ### Renaming with prefix and suffix text **Note:** This feature is only supported when using TypeScript 3.4+ By default, renames will not change shorthand property assignments or add aliases to import & export specifiers. For example, renaming the `a` variable declaration to `b`... ```ts const a = 5; const x = { a }; export { a }; ``` ...will do the following: ```ts const b = 5; const x = { b }; export { b }; ``` This behaviour can be changed by enabling the `usePrefixAndSuffixText` setting, which will do the following: ```ts const b = 5; const x = { a: b }; export { b as a }; ``` This behaviour change can be specified when renaming: ```ts setup: let varA: VariableDeclaration; varA.rename("SomeOtherName", { usePrefixAndSuffixText: true, }); ``` Or globally: ```ts const project = new Project({ manipulationSettings: { usePrefixAndSuffixTextForRename: true, }, }); // or project.manipulationSettings.set({ usePrefixAndSuffixTextForRename: true, }); ``` ### Renaming Files or Directories See: - [Moving Files](../details/source-files#move) - [Moving Directories](../navigation/directories#moving) --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/settings.md # Path: docs/manipulation/settings.md --- title: Manipulation Settings --- ## Manipulation Settings The manipulation settings can be set when creating the main `Project` object: ```ts import { IndentationText, NewLineKind, Project, QuoteKind } from "ts-morph"; const project = new Project({ // these are the defaults manipulationSettings: { // TwoSpaces, FourSpaces, EightSpaces, or Tab indentationText: IndentationText.FourSpaces, // LineFeed or CarriageReturnLineFeed newLineKind: NewLineKind.LineFeed, // Single or Double quoteKind: QuoteKind.Double, // Whether to change shorthand property assignments to property assignments // and add aliases to import & export specifiers (see more information in // the renaming section of the documentation). usePrefixAndSuffixTextForRename: false, // Whether to use trailing commas in multi-line scenarios where trailing // commas would be used. useTrailingCommas: false, }, }); ``` You can only provide a partial of these settings if you wish: ```ts const project = new Project({ manipulationSettings: { indentationText: IndentationText.TwoSpaces }, }); ``` ### Details Get more details about the settings by looking at the `manipulationSettings` property on the main `Project` object: ```ts project.manipulationSettings.getIndentationText(); project.manipulationSettings.getNewLineKind(); project.manipulationSettings.getQuoteKind(); project.manipulationSettings.getUsePrefixAndSuffixTextForRename(); ``` ### Updating You can update these settings later if you wish by using the `set` method: ```ts // set only one project.manipulationSettings.set({ quoteKind: QuoteKind.Single }); // or multiple project.manipulationSettings.set({ quoteKind: QuoteKind.Single, indentationText: IndentationText.TwoSpaces, }); ``` ### Formatting There are some additional manipulation settings that are taken from the `ts.FormatCodeSettings`. They will slowly be supported and added to the manipulation settings. For example: ```ts project.manipulationSettings.set({ // only one for now... will add more in the future insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: false, // default: true }); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/structures.md # Path: docs/manipulation/structures.md --- title: Structures --- ## Structures Simplified AST representations called _structures_ can be retreived from and used to set many `Node` objects. ### Getting structure To get the structure of a node, call `node.getStructure()`. ```ts // example with a class declaration, but this also works on interfaces, enums, and many other nodes. const classStructure = classDeclaration.getStructure(); // returns: ClassDeclarationStructure ``` In the example above, a class declaration like the following... ```ts export class MyClass { myProp = 5; } ``` ...would return the following structure object similar to the following: ```js { isAbstract: false, isExported: true, name: "MyClass", typeParameters: [], constructors: [], properties: [{ name: "myProp", initializer: "5", type: undefined, isReadonly: false, isStatic: false }], methods: [] } ``` ### Setting with structure It's also possible to set the structure of a node with an existing structure: ```ts setup: const classStructure = {}; classDeclaration.set(classStructure); // sets the name classDeclaration.set({ name: "NewName" }); // sets the properties classDeclaration.set({ properties: [{ name: "newProperty" }] }); ``` Or you can use the `addX` or `insertX` methods with a structure: ```ts sourceFile.addClass({ name: "NewClass", ...classDeclaration.getStructure() }); ``` ### Traversing structures #### `Structure` type guards Similar to static methods found on `Node`, there is also a `Structure` export that you can use to check certain information about a structure. For example: ```ts setup: const structure: Structures; import { Structure } from "ts-morph"; // ...etc... if (Structure.isExportable(structure)) structure.isExported = false; ``` #### `forEachStructureChild` Similar to the compiler API's `forEachChild`, there is a `forEachStructureChild` method in ts-morph for navigating over a structure's children. For example: ```ts import { forEachStructureChild, SourceFileStructure, Structure } from "ts-morph"; const structure: SourceFileStructure = { kind: StructureKind.SourceFile, statements: [{ kind: StructureKind.Function, name: "myFunction", parameters: [{ name: "myParam" }], }], }; forEachStructureChild(structure, child => { if (Structure.hasName(child)) console.log(child.name); }); ``` Outputs: `"myFunction"` ##### Structures with no kind Some structures have optional kinds. For example, in `parameters: [{ name: "myParam" }]` above, specifying `kind: StructureKind.Parameter` in the parameter would be unnecessarily repetitive. However, when using `forEachStructureChild`, you probably want to know the `kind` of the structure in order to do certain operations. For this reason, `forEachStructureChild` will automatically add the correct `kind` property to structures that don't have one. ##### Finding a child structure Note that unlike ts-morph's `forEachChild`, this function acts like the `forEachChild` in the compiler API and will return any truthy value returned in the second argument's function: ```ts setup: const structure: SourceFileStructure; const firstClassDecStructure = forEachStructureChild(structure, child => Structure.isClass(child) ? child : undefined); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/manipulation/transforms.md # Path: docs/manipulation/transforms.md --- title: Transforms --- ## Transforms It is possible to transform the AST using the compiler API, though this is not a typical scenario. For example: ```ts import { ts } from "ts-morph"; const project = new Project(); const sourceFile = project.createSourceFile("Example.ts", "1; 2; 3;"); // this can be done starting on any node and not just the root node sourceFile.transform(traversal => { const node = traversal.visitChildren(); // return type is `ts.Node` if (ts.isNumericLiteral(node)) { const incrementedValue = parseInt(node.text, 10) + 1; return traversal.factory.createNumericLiteral(incrementedValue.toString()); } return node; }); // outputs: 2; 3; 4; console.log(sourceFile.getFullText()); ``` Doing this is more performant, but you won't have type checking, symbols, and you'll be dealing directly with the TypeScript compiler API nodes. Additionally, all previously wrapped descendant nodes of transformed nodes will be forgotten (using them will result in an error being thrown). ### Conditionally visiting children ```ts import { ts } from "ts-morph"; const project = new Project(); const sourceFile = project.createSourceFile( "Example.ts", ` class C1 { myMethod() { function nestedFunction() { } } } class C2 { prop1: string; } function f1() { console.log("1"); function nestedFunction() { } }`, ); sourceFile.transform(traversal => { // this will skip visiting the children of the classes if (ts.isClassDeclaration(traversal.currentNode)) return traversal.currentNode; const node = traversal.visitChildren(); if (ts.isFunctionDeclaration(node)) { return traversal.factory.updateFunctionDeclaration( node, [], [], undefined, traversal.factory.createIdentifier("newName"), [], [], undefined, traversal.factory.createBlock([]), ); } return node; }); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/ambient-modules.md # Path: docs/navigation/ambient-modules.md --- title: Ambient Modules --- ## Ambient Modules The ambient module symbols can be retrieved by calling: ```ts const ambientModules = project.getAmbientModules(); ``` This will return the ambient modules resolved by the compiler (ex. ambient modules in `@types` or `node_modules`). ### Getting by name Get an ambient module symbol based on its name: ```ts const jQuerySymbol = project.getAmbientModule("jquery"); // returns: Symbol | undefined const momentSymbol = project.getAmbientModuleOrThrow("moment"); // returns: Symbol ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/compiler-nodes.md # Path: docs/navigation/compiler-nodes.md --- title: Underlying Compiler Nodes --- ## Underlying Compiler Nodes Sometimes it might be useful to get the node from the TypeScript compiler. They're accessible via the `.compilerNode` property that's found on all node objects: ```ts const compilerNode = interfaceDeclaration.compilerNode; ``` **Warning:** When manipulating the AST via this library, the underlying TypeScript AST tree is regenerated each time. For this reason, it's important not to hold on to TypeScript compiler nodes between manipulations or you could end up working with out of date information. ### Compiler node properties Sometimes there isn't a helper function in this library for accessing certain properties on the underlying compiler node. In these situations, you can access any underlying compiler node property by using the `.getNodeProperty(propName)` method: ```ts const nameNode = propertyAccessExpression.getNodeProperty("name"); // returns: PropertyName // also works with arrays and possibly undefined properties const typeParameters = classDeclaration.getNodeProperty("typeParameters"); // returns: TypeParameterDeclaration[] | undefined ``` ...and then please open an issue so that I can implement something I forgot to implement. ## Navigating Existing Compiler Nodes Sometimes you might want to easily navigate an existing compiler node. Do that by using the `createWrappedNode` function: ```ts ignore-error: 1109 import { createWrappedNode, ClassDeclaration, ts, SyntaxKind } from "ts-morph"; // some code that creates a class declaration using the ts object const classNode: ts.ClassDeclaration = ...; // create and use a wrapped node const classDec = createWrappedNode(classNode).asKindOrThrow(SyntaxKind.ClassDeclaration); const firstProperty = classDec.getProperties()[0]; // ... do more stuff here ... ``` **Note:** This is a lightweight way to navigate a node, but there are certian functionalities which will throw an error since there is no language service, type checker, or program. For example, finding references will not work because that requires a language service. ### Providing Type Checker If you would like to easily get the type information of the types in the provided source file, then provide a type checker: ```ts ignore-error: 1109 // given an existing node and type checker const classNode: ts.ClassDeclaration = ...; const compilerTypeChecker: ts.TypeChecker = ...; // create and use a wrapped node const classDec = createWrappedNode(classNode, { typeChecker: compilerTypeChecker }).asKindOrThrow(SyntaxKind.ClassDeclaration); console.log(classDec.getPropertyOrThrow("propName").getType().getText()); // ok, because a type checker was provided ``` ### Important: Using both the TypeScript API and ts-morph It is highly recommended to always use the `ts` named export from ts-morph when needing to use the TypeScript Compiler API and ts-morph at the same time: ```ts // do this import { ts } from "ts-morph"; // not this import * as ts from "typescript"; ``` They're almost identical and the `ts` named export from ts-morph should serve your needs. There's lots of reasons why this is done and it's outlined in [#333](https://github.com/dsherret/ts-morph/issues/333#issuecomment-391182952). --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/directories.md # Path: docs/navigation/directories.md --- title: Directories --- ## Directories Based on the source files created, appropriate directory objects will be created. These objects hold source files and directories that have been added. Note that it's completely fine to ignore the concept of directories and only deal with source files. This is an advanced feature that only needs to be used if it will help make solving your problem easier. ### Retrieving Directories can be retrieved from a source file: ```ts const directory = sourceFile.getDirectory(); ``` From other directories: ```ts directory.getDirectory("childDir"); directory.getDirectoryOrThrow("childDir"); // child directories directory.getDirectories(); // parent directory, if it exists directory.getParent(); ``` Or from the main `project` object: ```ts project.getRootDirectories(); // gets directories without a parent project.getDirectories(); // gets all the directories project.getDirectory("path/to/directory"); project.getDirectoryOrThrow("path/to/directory"); ``` ### Adding On a directory: ```ts const childDirectory = directory.addDirectoryAtPath("childDir"); // or addDirectoryAtPathIfExists ``` Or main `project` object: ```ts const directory = project.addDirectoryAtPath("path/to/dir"); // or addDirectoryAtPathIfExists ``` ### Creating On a directory ```ts const childDir = directory.createDirectory("childDir"); ``` Or main `project` object: ```ts const directory = project.createDirectory("path/to/dir"); ``` ## Directory ### Path and name ```ts // returns the full path (ex. /home/david/project/) const path = directory.getPath(); // returns only the directory name (ex. project) const baseName = directory.getBaseName(); ``` ### Parent directory ```ts const parentDir = directory.getParent(); // or getParentOrThrow() ``` ### Child directories ```ts const childDirs = directory.getDirectories(); ``` ### Ancestor / Descendant Check if a directory is an ancestor or descendant of another directory: ```ts setup: let grandParentDir: Directory, childDir: Directory; grandParentDir.isAncestorOf(childDir); // true childDir.isDescendantOf(grandParentDir); // true ``` Or if a directory is an ancestor of a source file: ```ts setup: let grandParentDir: Directory, parentDir: Directory, childSourceFile: SourceFile; grandParentDir.isAncestorOf(childSourceFile); // true parentDir.isAncestorOf(childSourceFile); // true ``` ### Source files ```ts const sourceFiles = directory.getSourceFiles(); const sourceFile = directory.getSourceFile("someFile.ts"); // or getSourceFileOrThrow const indexFile = directory.addSourceFileAtPath("index.ts"); // or addSourceFileAtPathIfExists const descendantSourceFiles = directory.getDescendantSourceFiles(); directory.createSourceFile("someFile.ts"); directory.createSourceFile("someFile2.ts", "// some text"); directory.createSourceFile("someFile3.ts", writer => writer.writeLine("// some text")); directory.createSourceFile("someFile4.ts", { statements: [{ kind: StructureKind.Enum, name: "MyEnum" }] }); ``` ### Saving Save the directory to the disk and all the unsaved source files: ```ts await directory.save(); directory.saveSync(); // slow ``` ### Emitting It's possible to only specific directories: ```ts // always check result.getEmitSkipped() to make sure the emit was successful const result = await directory.emit(); directory.emitSync(); // slow ``` Or specify the output directories (specify a path relative from the directory or an absolute paths): ```ts directory.emit({ outDir: "out", declarationDir: "declarations", }); ``` And of course, specify to only emit declaration files: ```ts directory.emit({ emitOnlyDtsFiles: true }); ``` ### Moving Move the directory to a new directory: ```ts setup: const otherDir: Directory; // ex. moves C:\MyProject\dir to C:\MyProject\newDir if working directory is C:\MyProject directory.move("./newDir"); // ex. moves C:\MyProject\newDir to C:\MyProject\otherDir using a relative path directory.move("../otherDir", { overwrite: true }); // allows overwriting (otherwise it will throw) // or specify an absolute path directory.move("C:\\finalDir"); // or specify the directory to move to directory.moveToDirectory("some/directory"); directory.moveToDirectory(otherDir); ``` ### Moving Immediately Moving a directory immediately can be done by using one of the following methods: ```ts await directory.moveImmediately("../newDir"); // or directory.moveImmediatelySync("../newDir2"); ``` ### Copying Copy the directory to a new directory: ```ts setup: const otherDir: Directory; // ex. copies C:\MyProject\dir to C:\MyProject\newDir directory.copy("../newDir"); // allows overwriting (otherwise it will throw) directory.copy("../nextDir", { overwrite: true }); // or specify an absolute path directory.copy("C:\\test"); // or specify the directory to copy to directory.copyToDirectory("some/directory"); directory.copyToDirectory(otherDir); ``` Note that the directory and source files, in all these cases, won't be created until calling save on the project. #### Not including untracked files When moving a directory, it will queue up a file system copy for the directory from to the directory to. If you only wish to copy the source files that are found in memory within the directory, then set the `includeUntrackedFiles` option to false: ``` directory.copy("../finalDir", { includeUntrackedFiles: false }); ``` ### Copying Immediately Copying a directory immediately can be done by using one of the following methods: ```ts await directory.copyImmediately("../otherDir"); // or directory.copyImmediatelySync("../otherDir2"); ``` ### Deleting Call: ```ts directory.delete(); ``` This will remove the directory object and all its descendant source files and directories from the main `project` object and queue it up for deletion to the file system. When you're all done your other manipulations, call `project.save()` and at that point the directory will be deleted. #### Deleting immediately If you want to delete a directory immediately from the file system, then use the following: ```ts await directory.deleteImmediately(); // or directory.deleteImmediatelySync(); ``` This isn't recommended though because it could possibly leave the file system in a halfway state if your code errors before it's done. ### Clearing Call: ```ts directory.clear(); ``` This will delete the directory's descendants in memory and queue a delete and mkdir operation to the file system. #### Clearing immediately If you want to do this operation immediatley to the file system, then use the following: ```ts await directory.clearImmediately(); // or directory.clearImmediatelySync(); ``` This isn't recommended though because it could possibly leave the file system in a halfway state if your code errors before it's done. ### Forgetting Forgets the directory from main project object without deleting it: ```ts directory.forget(); ``` Note that after doing this, the directory object and all its descendant source files and directories will not be available. If you want to use them again, then you will need to re-add them. ### Relative File Paths It might be useful to get the relative path from one directory to another source file or directory. ```ts setup: let directoryFrom: Directory, sourceFileTo: SourceFile; const relativePath = directoryFrom.getRelativePathTo(sourceFileTo); ``` Or to get the module specifier text from one directory to another source file or directory. ```ts setup: let directoryFrom: Directory, sourceFileTo: SourceFile; const moduleSpecifier = directoryFrom.getRelativePathAsModuleSpecifierTo(sourceFileTo); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/example.md # Path: docs/navigation/example.md --- title: Navigation example --- ## Example - Navigating Within Source Files ### Setup Given the following file: ```ts // Person.ts interface Person { name: string; age: number; } export default Person; ``` And setup: ```ts import { Project } from "ts-morph"; const project = new Project(); project.addSourceFilesAtPaths("**/*.ts"); ``` ### Use First you need to get the source file you would like to look at: ```ts const sourceFile = project.getSourceFileOrThrow("Person.ts"); ``` Now inspect what's inside... here's a few examples: ```ts const hasClasses = sourceFile.getClasses().length > 0; const interfaces = sourceFile.getInterfaces(); // person interface const personInterface = sourceFile.getInterface("Person")!; personInterface.isDefaultExport(); // returns true personInterface.getName(); // returns "Person" personInterface.getProperties(); // returns the properties ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/finding-references.md # Path: docs/navigation/finding-references.md --- title: Finding References --- ## Finding References Find all the references of a node by calling `.findReferences()` on an identifier or named/nameable declaration. ### Example Simple example: ```ts ignore-error: 1109 const classDeclaration = ...; // get a class or some other declaration somehow const referencedSymbols = classDeclaration.findReferences(); for (const referencedSymbol of referencedSymbols) { for (const reference of referencedSymbol.getReferences()) { console.log("---------") console.log("REFERENCE") console.log("---------") console.log("File path: " + reference.getSourceFile().getFilePath()); console.log("Start: " + reference.getTextSpan().getStart()); console.log("Length: " + reference.getTextSpan().getLength()); console.log("Parent kind: " + reference.getNode().getParentOrThrow().getKindName()); console.log("\n"); } } ``` ## Finding Referencing Nodes The `.findReferences()` method returns back a lot of information that might not be necessary. If you just want the nodes that reference the named/nameable declaration, then use the following method: ```ts const nodes = classDeclaration.findReferencesAsNodes(); ``` ## "Go to Definition" Similar to finding references, you can also go to an identifier's definitions: ```ts const definitions = identifier.getDefinitions(); ``` Or just get the nodes: ```ts const nodes = identifier.getDefinitionNodes(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/getting-source-files.md # Path: docs/navigation/getting-source-files.md --- title: Getting Source Files --- ## Getting Source Files After source files are added, you will need to get them in order to navigate or make changes. ### All Get all the source files: ```ts const sourceFiles = project.getSourceFiles(); ``` Or filter by glob: ```ts // single const testSourceFiles = project.getSourceFiles("src/test/**/*.ts"); // or multiple const nonTestSourceFiles = project.getSourceFiles([ "src/**/*.ts", "!src/test/**/*.ts", ]); ``` Note that the path resolution and matching is done relative to the current working directory. For example, if you load a project from another directory... ```ts const project = new Project({ tsConfigFilePath: `/someDirectory/notCurrent/tsconfig.json`, }); ``` ...to match a file from that directory you need to specify a glob that matches based on the current working directory or absolute path... ```ts const sourceFile = project.getSourceFiles(`/someDirectory/notCurrent/**/config/index.ts`); ``` ### By file path Will return the first source file that matches the end of the provided file path: ```ts const personFile = project.getSourceFile("Models/Person.ts"); ``` ### By condition Will return the first source file that matches the provided condition: ```ts const fileWithFiveClasses = project.getSourceFile(f => f.getClasses().length === 5); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/index.md # Path: docs/navigation/index.md --- title: Navigating the AST --- ## Navigating the AST Navigating the AST should be simple and straightforward. Right now, the best way to explore what's implemented is to look at the autocompletion/intellisense results or view [this report](https://github.com/dsherret/ts-morph/blob/latest/packages/ts-morph/wrapped-nodes.md). If you can't find something that means it's most likely not implemented and you should [open an issue](https://github.com/dsherret/ts-morph/issues) on GitHub. ### General methods Search autocomplete for methods like `.getChildren()`, `.getParent()`, `.getFirstChildBySyntaxKind(kind)`, etc... Many exist. If you find one you would really like, then please [open an issue](https://github.com/dsherret/ts-morph/issues). ### getChildren() and forEachChild(child => ...) In general, you can easily navigate the tree by using methods such as `.getClasses()`, `.getClass('MyClass')`, `.getModules()`, and so on, but in some cases you might want to get all the child nodes. In the compiler API, there exists a `node.getChildren()` method and `ts.forEachChild(node, child => { })`/`node.forEachChild(child => { })` function/method. - `.getChildren()` - Returns all the children including the all the tokens (ex. `OpenBraceToken`, `SemiColonToken` etc.). - `.forEachChild(child => {})` - Iterates all the child nodes that are properties of the node. [![getChildren vs forEachChild](images/getChildrenVsForEachChild.gif)](http://ts-ast-viewer.com) In ts-morph, these methods also exist and they can be used similarly to the compiler API: ```ts const allChildren = node.getChildren(); node.forEachChild(node => { console.log(node.getText()); }); const classDec = node.forEachChild(node => { if (Node.isClassDeclaration(node)) return node; // stops iterating over the children and returns this value return undefined; // return a falsy value or no value to continue iterating }); ``` ### forEachDescendant If you wish to iterate all the descendants, then use the `forEachDescendant` method: ```ts node.forEachDescendant(node => console.log(node.getText())); ``` This is especially useful when writing code that implements a visitor pattern: ```ts ignore-error: 1109, setup: let sourceFiles: SourceFile[]; interface Visitor { visit(node: Node): void; } const myVisitors: Visitor[] = ...; for (const sourceFile of sourceFiles) sourceFile.forEachDescendant(node => myVisitors.forEach(v => v.visit(node))); ``` #### Traversal Control Traversal can be controlled with the second parameter: ```ts const result = node.forEachDescendant((node, traversal) => { switch (node.getKind()) { case SyntaxKind.ClassDeclaration: // skips traversal of the current node's descendants traversal.skip(); break; case SyntaxKind.Parameter: // skips traversal of the current node's descendants and its siblings and all their descendants traversal.up(); break; case SyntaxKind.FunctionDeclaration: // stops traversal completely traversal.stop(); break; case SyntaxKind.InterfaceDeclaration: // stops traversal completely and returns this value return node; } return undefined; }); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/language-service.md # Path: docs/navigation/language-service.md --- title: Language Service --- ## Language Service Get the language service by calling: ```ts const languageService = project.getLanguageService(); ``` ### Underlying compiler object The underlying `ts.LanguageService` can be retrieved as follows: ```ts const tsLanguageService = languageService.compilerObject; ``` ### Use Generally you won't need to use the language service because most of the functionality is exposed as methods on other objects. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/program.md # Path: docs/navigation/program.md --- title: Program --- ## Program Get the program by calling: ```ts const program = project.getProgram(); ``` ### Underlying compiler object The underlying `ts.Program` can be retrieved as follows: ```ts const tsProgram = program.compilerObject; ``` **Warning:** The underlying compiler object will be discared whenever manipulation occurs. For that reason, only hold onto the underlying compiler object between manipulations. ### Use Generally you won't need to use the program because most of the functionality is exposed as methods on other objects. --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/navigation/type-checker.md # Path: docs/navigation/type-checker.md --- title: Type Checker --- ## Type Checker Get the type checker by calling: ```ts const typeChecker = project.getTypeChecker(); ``` ### Underlying compiler object The underlying `ts.TypeChecker` can be retrieved as follows: ```ts const tsTypeChecker = typeChecker.compilerObject; ``` **Warning:** The underlying compiler object will be discared whenever manipulation occurs. For that reason, only hold onto the underlying compiler object between manipulations. ### Use Generally you won't need to use the type checker because most of the functionality is exposed as methods on other objects. ### Signature Resolution Get the resolved signature of a call-like expression node (ex. call expression): ```ts const resolvedSignature = typeChecker.getResolvedSignature(callLikeExpression); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/readme.md # Path: readme.md # ts-morph [![CI](https://github.com/dsherret/ts-morph/workflows/CI/badge.svg)](https://github.com/dsherret/ts-morph/actions?query=workflow%3ACI) Monorepo for [ts-morph](packages/ts-morph) and related projects. ## Packages - [ts-morph](packages/ts-morph) - TypeScript Compiler API wrapper. Provides an easier way to programmatically navigate and manipulate TypeScript and JavaScript code. - [@ts-morph/bootstrap](packages/bootstrap) - Separate library for getting quickly setup with the Compiler API. ## Resources - [TypeScript AST Viewer](https://ts-ast-viewer.com) --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/setup/adding-source-files.md # Path: docs/setup/adding-source-files.md --- title: Adding Source Files --- ## Adding Source Files You will need to populate the `project` object with source files. ### By a _tsconfig.json_ Source files will be added when instantiating with a `tsConfigFilePath`: ```ts import { Project } from "ts-morph"; const project = new Project({ tsConfigFilePath: "path/to/tsconfig.json", }); ``` ...and this can be disabled by setting `skipAddingFilesFromTsConfig: true`: ```ts const project = new Project({ tsConfigFilePath: "path/to/tsconfig.json", skipAddingFilesFromTsConfig: true, }); ``` Alternatively, populate the `project` object by calling `addSourceFilesFromTsConfig`: ```ts project.addSourceFilesFromTsConfig("path/to/tsconfig.json"); ``` #### Source File Dependency Resolution By default, all the source files added to the project in the constructor via a _tsconfig.json_ will automatically be analyzed to include the source files they depend on. If you wish to skip this analysis step, then provide the `skipFileDependencyResolution` option: ```ts const project = new Project({ tsConfigFilePath: "path/to/tsconfig.json", skipFileDependencyResolution: true, }); ``` If you are adding source files to a project in other ways and want to ensure the all the source files depended on by the added source files are also included in the Project, then call the `.resolveSourceFileDependencies()` after adding everything: ```ts const project = new Project(); // add everything to the project project.addSourceFilesFromTsConfig("dir1/tsconfig.json"); project.addSourceFilesFromTsConfig("dir2/tsconfig.json"); project.addSourceFilesAtPaths("dir3/**/*{.d.ts,.ts}"); // optionally call this when complete to resolve and // add the dependent source files to the project project.resolveSourceFileDependencies(); ``` ### By file globs or file paths Specify as many file globs or file paths as you wish: ```ts project.addSourceFilesAtPaths("folder/**/*{.d.ts,.ts}"); project.addSourceFilesAtPaths(["folder/file.ts", "folder/otherFile.ts"]); project.addSourceFilesAtPaths(["**/*.ts", "!**/*.d.ts"]); ``` ### By file path ```ts const sourceFile = project.addSourceFileAtPath("path/to/file.ts"); // or addSourceFileAtPathIfExists ``` ### By structure Create source files based on an object that looks like the AST of a source file: ```ts const sourceFile = project.createSourceFile("path/to/myStructureFile.ts", { statements: [{ kind: StructureKind.Enum, name: "MyEnum", members: [{ name: "member", }], }, { kind: StructureKind.Class, name: "MyClass", // etc... }], // etc... }); ``` The above would create a source file with the following text: ```ts enum MyEnum { member, } class MyClass { } ``` ### By string ```ts const fileText = "enum MyEnum {\n}\n"; const sourceFile = project.createSourceFile("path/to/myNewFile.ts", fileText); ``` ### By writer function ```ts const sourceFile = project.createSourceFile("path/to/myOtherNewFile.ts", writer => { writer .writeLine("import * as ts from 'typescript';").blankLine() .writeLine("export class MyClass {}"); }); ``` ### Options `createSourceFile` will throw an error if the file already exists. To not throw an error, set the `overwrite` option to true. ```ts const sourceFile = project.createSourceFile("path/to/myNewFile.ts", "", { overwrite: true }); ``` ### Note Adding source files to the project from a structure, writer function, or text will act like any other source file, but they will not be saved to the disk unless you ask it to be. ```ts // save it to the disk if you wish: await sourceFile.save(); // or saveSync(); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/setup/ast-viewers.md # Path: docs/setup/ast-viewers.md --- title: AST Viewers --- ## AST Viewers An AST viewer is a useful way to help understand the TypeScript AST for some source code. ### TypeScript AST Viewer I've created this very basic web-based TypeScript AST viewer. [TypeScript AST Viewer](http://ts-ast-viewer.com) Features: - View code on left, tree in middle, and information about selected node on right. - View the type and symbol of the selected node. - Toggle the tree between `node.forEachChild(...)` and `node.getChildren()`. - Change compiler API versions. - Use some compiler objects in the browser console. [![TypeScript AST Viewer](images/ts-ast-viewer.png)](http://ts-ast-viewer.com) I will improve and add more functionality to this in the future. You can help contribute to its progress [here](https://github.com/dsherret/ts-ast-viewer). ### Atom TypeScript This AST viewer gives an excellent view of the AST. 1. Install [Atom](https://atom.io/). 2. Install [atom-typescript](https://atom.io/packages/atom-typescript). 3. Create a new typescript file. 4. Paste in your typescript code. ![TypeScript file](images/atom-file.png) 5. Important: Ensure the current typescript document has focus. 6. Open the command palette (Windows/Linux: `ctrl+shift+p`, Mac: `cmd+shift+p`). 7. Type `TypeScript: Ast Full` and hit enter (or run `TypeScript: Ast` to get the ast without tokens). ![Command Palette](images/atom-command-palette.png) 8. A new tab will appear with the AST. [![atom-typescript AST Viewer](images/atom-ast_small.png)](images/atom-ast.png) --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/setup/diagnostics.md # Path: docs/setup/diagnostics.md --- title: Diagnostics --- ## Diagnostics Diagnostics (compile errors) can be retrieved on the project or on source files: ```ts const diagnostics = project.getPreEmitDiagnostics(); // or on a source file const sourceFileDiagnostics = sourceFile.getPreEmitDiagnostics(); ``` The pre-emit diagnostics are the syntactic, semantic, global, options, config file parsing, and if enabled the declaration diagnostics. ### Formatting for Output To nicely output the diagnostics, use `project.formatDiagnosticsWithColorAndContext`: ```ts const diagnostics = project.getPreEmitDiagnostics(); console.log(project.formatDiagnosticsWithColorAndContext(diagnostics)); ``` ### Diagnostic #### Message text Returned message text could be a `string` or a `DiagnosticMessageChain`: ```ts const message = diagnostic.getMessageText(); ``` #### Source file Source file the diagnostic occurs in: ```ts const sourceFile = diagnostic.getSourceFile(); // returns: SourceFile | undefined ``` #### Start, line number, & length Position in the file, the line number, and length of the diagnostic: ```ts const start = diagnostic.getStart(); // returns: number const lineNumber = diagnostic.getLineNumber(); // returns: number const length = diagnostic.getLength(); // returns: number ``` #### Category Categories can be warnings, errors, or just messages. ```ts const category = diagnostic.getCategory(); // returns: DiagnosticCategory ``` #### Code This is the error code number: ```ts const code = diagnostic.getCode(); // returns: number ``` #### Source todo: I don't know what this is, but it's available to get from the diagnostic. ```ts const source = diagnostic.getSource(); // returns: string | undefined ``` ### DiagnosticMessageChain A diagnostic message chain (DMC) will be returned by `diagnostic.getMessageText()` in certain scenarios. According to the typescript compiler: ``` /** * A linked list of formatted diagnostic messages to be used as part of a multiline message. * It is built from the bottom up, leaving the head to be the "main" diagnostic. * While it seems that DiagnosticMessageChain is structurally similar to DiagnosticMessage, * the difference is that messages are all preformatted in DMC. */ ``` The properties of a DMC are similar to a Diagnostic: ```ts const messageText = dmc.getMessageText(); // returns: string const category = dmc.getCategory(); // returns: DiagnosticCategory const code = dmc.getCode(); // returns: number ``` #### Next DMC in linked list Call `.getNext()`: ```ts const next = dmc.getNext(); // returns: DiagnosticMessageChain | undefined ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/setup/file-system.md # Path: docs/setup/file-system.md --- title: File System --- ## File System By default, the library will use the local file system based on the current working directory. In most scenarios, you won't have to bother with what's outlined here, but it may be useful in some scenarios (for example, using an in-memory file system is useful for mocking the file system for testing purposes). ### Current File System Object ```ts import { Project } from "ts-morph"; const project = new Project(); const fs = project.getFileSystem(); // returns: FileSystemHost ``` This file system object can be used to interact with the current file system. The methods available on it are very obvious and not worth explaining here (ex. `writeFile(filePath: string, fileText: string): Promise`, `readFile(filePath: string): Promise`, `readFileSync(filePath: string): string`, etc..). ### In-Memory File System If you want to use a file system that is stored in memory, specify that when creating a `Project` instance: ```ts import { Project } from "ts-morph"; const project = new Project({ useInMemoryFileSystem: true }); const fs = project.getFileSystem(); const sourceFile = project.createSourceFile("file.ts", "console.log(5);"); sourceFile.saveSync(); console.log(fs.readFileSync("file.ts")); // outputs: "console.log(5);" ``` The current working directory on this file system will be `/`. This file system can also be imported and created via the `InMemoryFileSystemHost` export. #### Remember: The Default Script Target is `ES5` You may wonder why certain standard types are `any` when using an in memory file system: ```ts const project = new Project({ useInMemoryFileSystem: true }); const sourceFile = project.createSourceFile( "index.ts", `const mySet = new Set();`, ); const mySetDecl = sourceFile.getVariableDeclarationOrThrow("mySet"); console.log(mySetDecl.getType().getText()); // any ``` This is because, the `lib` compiler option must be specified, similar to when you use `tsc`: ```ts setup: let mySetDecl: Node; const project = new Project({ useInMemoryFileSystem: true, compilerOptions: { lib: ["lib.es2015.d.ts"], }, }); /// ...omitted... same as above... console.log(mySetDecl.getType().getText()); // Set, good ``` Or you may specify a target that will implicitly load in the lib files that you need: ```ts setup: let mySetDecl: Node; import { Project, ts } from "ts-morph"; const project = new Project({ useInMemoryFileSystem: true, compilerOptions: { target: ts.ScriptTarget.ES2015, }, }); /// ...omitted... same as above... console.log(mySetDecl.getType().getText()); // Set, good ``` Note that if you want to include all the lib files, you may specify `lib.esnext.full.d.ts` as a `lib` option: ```ts const project = new Project({ useInMemoryFileSystem: true, compilerOptions: { lib: ["lib.esnext.full.d.ts"], }, }); ``` ### Custom File System It's possible to use your own custom file system by implementing the `FileSystemHost` interface then passing in an instance of this when creating a new `Project` instance: ```ts ignore-error: 2420, 2345, 2740 import { FileSystemHost, Project } from "ts-morph"; class MyCustomFileSystem implements FileSystemHost { // implement it } const fs = new MyCustomFileSystem(); const project = new Project({ fileSystem: fs }); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/setup/index.md # Path: docs/setup/index.md --- title: Instantiating --- ## Instantiating Use the `Project` named export from `"ts-morph"`: ```ts import { Project } from "ts-morph"; const project = new Project(); ``` ### Compiler options ```ts import { Project, ScriptTarget } from "ts-morph"; const project = new Project({ compilerOptions: { target: ScriptTarget.ES3, }, }); ``` ### tsconfig.json If you would like to manually specify the path to a _tsconfig.json_ file then specify that: ```ts const project = new Project({ tsConfigFilePath: "path/to/tsconfig.json", }); ``` _Note:_ You can override any `tsconfig.json` options by also providing a `compilerOptions` object. For your convenience, this will automatically add all the associated source files from the _tsconfig.json_. If you don't wish to do that, then you will need to set `skipAddingFilesFromTsConfig` to `true`: ```ts const project = new Project({ tsConfigFilePath: "path/to/tsconfig.json", skipAddingFilesFromTsConfig: true, }); ``` ### Custom Module Resolution Custom module resolution can be specified by providing a resolution host factory function. This also supports providing custom type reference directive resolution. For example: ```ts import { Project, ts } from "ts-morph"; // this is deno style module resolution (ex. `import { MyClass } from "./MyClass.ts"`) const project = new Project({ resolutionHost: (moduleResolutionHost, getCompilerOptions) => { return { resolveModuleNames: (moduleNames, containingFile) => { const compilerOptions = getCompilerOptions(); const resolvedModules: ts.ResolvedModule[] = []; for (const moduleName of moduleNames.map(removeTsExtension)) { const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost); if (result.resolvedModule) resolvedModules.push(result.resolvedModule); } return resolvedModules; }, }; function removeTsExtension(moduleName: string) { if (moduleName.slice(-3).toLowerCase() === ".ts") return moduleName.slice(0, -3); return moduleName; } }, }); ``` ### `libFolderPath` By default, ts-morph uses a fake folder path at `/node_modules/typescript/lib` to serve the TypeScript lib.d.ts files from memory. If you do not want this behaviour, you may specify an actual folder to get the lib files from the file system from: ```ts const project = new Project({ libFolderPath: "./node_modules/typescript/lib", }); ``` --- # ts-morph Documentation # Source: https://raw.githubusercontent.com/dsherret/ts-morph/latest/docs/utilities.md # Path: docs/utilities.md --- title: Utilities --- ## Utilities This is an outline of any utilities currently available in the library. ### Get compiler options from tsconfig.json Get the compiler options from a file by using the `getCompilerOptionsFromTsConfig` function: ```ts import { getCompilerOptionsFromTsConfig } from "ts-morph"; const result = getCompilerOptionsFromTsConfig("file/path/to/tsconfig.json"); result.options; // compiler options result.errors; // diagnostics ``` ### Type Guards There is a collection of type guard functions that are useful for finding out the type of a node: ```ts import { Node } from "ts-morph"; // ... some code here that gets a node ... if (Node.isClassDeclaration(node)) { // node is of type ClassDeclaration in here } ``` ### Printing a Node Usually with the library, you can print any node by calling the `.print()` method: ```ts node.print(); // returns: string ``` But sometimes you might want to print a compiler node. There's a `printNode` utility function for doing that: ```ts import { printNode, ts } from "ts-morph"; // get a compiler node from somewhere const compilerNode: ts.Node = ...; // optionally provide a source file and there is some printing options on this const functionText = printNode(compilerNode); console.log(functionText); ```