View CHANGELOG.md for more detail on releases. This file is only a high level overview of breaking changes.
node.getFirstChildByKind
andnode.getChildrenOfKind
now search the parsed tree via.forEachChild(...)
when specifying a parsed node's syntax kind. Previously it would only search the results ofnode.getChildren()
.
Resolved node_module source files and directories are no longer returned from Project#getSourceFiles() and getDirectories()
Previously resolved source files in node_modules
would be returned when calling project.getSourceFiles()
. This was sometimes confusing and meant that people iterating over the source files in a project would need to ensure they weren't looking at the files in the node_modules
directory.
Now they are not added unless done so explicitly:
project.addExistingSourceFiles("node_modules/**/*.ts");
Note that the directory and source files are still available when you explicitly specify their path (ex. project.getDirectory("node_modules")
).
- Added support for TS 3.2.
- Removed
JSDocTag#getAtToken()
. TheatToken
property was removed in the compiler api.
Options
interface renamed toProjectOptions
in order to be more specific.
Referenced source files in module specifiers and references are now added to the project when constructing a project and providing a tsconfig.
For example, say you had the following files:
// src/main.ts
export * from "./classes";
// src/classes.ts
export class Test {}
// tsconfig.json
{
"files": ["src/main.ts"],
"compilerOptions" {
// etc...
}
}
Now when constructing a project like so...
const project = new Project({ tsConfigFilePath: "tsconfig.json" });
...the project will now include both source files instead of only src/main.ts.
Doing this requires an extra analysis step so if you want to revert back to the old behaviour, provide the skipFileDependencyResolution
option and set it to true:
const project = new Project({
tsConfigFilePath: "tsconfig.json",
skipFileDependencyResolution: true
});
Previously a custom file system host could be passed to the constructor like so:
const project = new Project({ }, fileSystem);
This was mostly for legacy reasons. It's now been moved to be an option.
const project = new Project({ fileSystem });
JSDocTag.getName()
is now.getTagName()
. This was originally incorrectly named and.getName()
is necessary for js doc tags that have one (such asJSDocPropertyLikeTag
).
- Removed
CompilerNodeBrandPropertyNamesType
type alias. - More nodes are being written with hanging indentation when doing a newline.
In ambient declarations, there exists namespace declarations that look like the following:
global {
export class Test {}
}
The following changes were made:
Deprecated:
.setHasNamespaceKeyword
.setHasModuleKeyword
NamespaceDeclarationStructure
-hasModuleKeyword
andhasNamespaceKeyword
Added:
enum NamespaceDeclarationKind { Namespace = "namespace", Module = "module", Global = "global" }
setDeclarationKind(kind: NamespaceDeclarationKind)
getDeclarationKind(): NamespaceDeclarationKind;
NamespaceDeclarationStructure
-declarationKind: NamespaceDeclarationKind
Even though it's a compile error, index signatures may look like the following:
interface MyInterface {
[key: string];
}
For this reason, the .getReturnTypeNode()
method on IndexSignatureDeclaration
may now return undefined.
Previously there were a lot of XExtensionType
type aliases that were used internally within the library, but they were
being exported. These are now not exported from the libraries declaration file and made internal. See #441
for more details.
TypeParameterDeclaration
-getConstraintNode()
andgetDefaultNode()
are nowgetConstraint()
andgetDefault()
.JsxTagNamedNode
-getTagName()
is now.getTagNameNode()
for consistency.
Previously, the following methods would rename with the language service:
ImportDeclaration.setDefaultImport
ImportSpecifier.setAlias
ExportSpecifier.setAlias
These no longer rename using the language service. Use the corresponding renameX
methods instead.
The .fill
methods are now called .set
and their behaviour has changed.
Previously calling...
classDeclaration.fill({
properties: [{ name: "newProp" }]
});
...would add a property. Now with .set
it will remove all existing properties and replace it with the specified properties.
If you want the .fill
behaviour, use the .addX
methods or provide the structures of the nodes by using .getStructure()
(Ex. classDeclaration.set({ properties: [...classDeclaration.getParameters().map(p => p.getStructure()), { name: "NewProperty" }] });
)`
Previously, doing classDeclaration.fill({ name: "NewName" })
would do a rename with the language service. Now with .set({ name: "NewName" })
it sets the name without renaming.
Use project.getPreEmitDiagnostics()
and sourceFile.getPreEmitDiagnostics()
instead. Read why in #384.
Also, deprecated program.getPreEmitDiagnostics()
. It didn't make sense for this method to be on Program
.
For example, given the following code:
const { a, b } = { a: 1, b: 2 };
Doing the following will now correctly return a binding element:
const statement = sourceFile.getVariableStatements()[0];
const declaration = statement.getDeclarations();
const name = declaration.getNameNode();
if (TypeGuards.isBindingElement(name))
console.log(name.getElements().map(e => e.getName())); // outputs: ["a", "b"]
CompilerApiNodeBrandPropertyNamesType
is nowCompilerNodeBrandPropertyNamesType
.renameName
onImportSpecifier
andExportSpecifier
is now deprecated. UseimportSpecifier.getNameNode().rename(newName)
.- Renamed
getAliasIdentifier()
togetAliasNode()
onImportSpecifier
andExportSpecifier
. Done for consistency. - Deprecated
node.getStartColumn()
andnode.getEndColumn()
. - Renamed
sourceFile.getColumnAtPos(pos)
to.getLengthFromLineStartAtPos(pos)
for correctness. - Renamed
sourceFile.getLineNumberFromPos(pos)
togetLineNumberAtPos(pos)
for consistency. getImplementations()[i].getNode()
now returns the identifier range (compiler API changed behaviour).
Similarly to ClassDeclaration
, FunctionDeclaration
can have an optional name when used as a default export:
export default function() {
}
To support this scenario, FunctionDeclaration
's name has become optional.
Previously, stopping traveral in node.forEachDescendant(...)
was done like the following:
node.forEachDescendant((node, stop) => {
if (node.getKind() === SyntaxKind.FunctionDeclaration)
stop();
});
The new code is the following:
node.forEachDescendant((node, traversal) => {
if (node.getKind() === SyntaxKind.FunctionDeclaration)
traversal.stop();
});
This is to allow for more advanced scenarios:
node.forEachDescendant((node, traversal) => {
switch (node.getKind()) {
case SyntaxKind.ClassDeclaration:
// skips traversal of the current node's descendants
traversal.skip();
break;
case SyntaxKind.ParameterDeclaration:
// skips traversal of the current node, siblings, and all their descendants
traversal.up();
break;
case SyntaxKind.FunctionDeclaration:
// stop traversal completely
traversal.stop();
break;
}
});
Also, take note that node.forEachChild
has been updated for consistency with forEachDescendant
:
node.forEachChild((node, traversal) => {
if (node.getKind() === SyntaxKind.FunctionDeclaration)
traversal.stop();
});
The declaration file now uses some conditional types and that requires TypeScript 2.8+.
Methods in the form of Type.isXType()
are now Type.isX()
. For example:
type.isArrayType();
Is now:
type.isArray();
This was done so it aligns with the TypeScript compiler API methods and to remove the needless repetition in the naming.
getReferencingNodes()
onReferenceFindableNode
s is nowfindReferencesAsNodes()
andlanguageService.getDefinitionReferencingNodes()
is also nowfindReferencesAsNodes()
. This was done to improve its discoverability.
Instead of access code-block-writer by doing:
import CodeBlockWriter from "code-block-writer";
It's now a named export from this library:
import { CodeBlockWriter } from "ts-morph";
The FileSystemHost
interface now has move
, moveSync
, copy
, and copySync
methods.
Directory
.remove()
is now.forget()
- This was done for consistency withSourceFile
.
SourceFile
getRelativePathToSourceFileAsModuleSpecifier
is nowgetRelativePathAsModuleSpecifierTo
.getRelativePathToSourceFile
is nowgetRelativePathTo
.
Identifier
getDefinitionReferencingNodes()
is nowgetReferencingNodes()
for consistency.
ClassDeclarationStructure
ctor
is nowctors
- There can be multiple constructors on ambient classes.
ExportAssignmentStructure
isEqualsExport
is nowisExportEquals
- I originally named this incorrectly by accident.
I had no idea you can have class declarations with no names...
export default class {
// ...etc...
}
...and so .getName()
and .getNameNode()
on ClassDeclaration
is now nullable. I recommend using .getNameOrThrow()
if you don't want to check for null each time or assert null.
Project
and Directory
addSourceFileIfExists
is nowaddExistingSourceFileIfExists
(standardizes withaddExistingSourceFile
)addDirectoryIfExists
is nowaddExistingDirectoryIfExists
(standardizes withaddExistingDirectory
)
SourceFile
getReferencingImportAndExportDeclarations
is removed. UsegetReferencingNodesInOtherSourceFiles
.
VariableDeclarationType
and QuoteType
are now called VariableDeclarationKind
and QuoteKind
respectively.
This was done to reduce the confusion with the word "type" and to standardize it with other enums.
Script target was removed from the manipulation settings (it was there for legacy reasons) and can now be found only on the compiler options.
Instead of being written as:
import {SomeExport} from "./SomeFile";
Imports will now be written with spaces (as is the default in the compiler):
import { SomeExport } from "./SomeFile";
If you don't want this behaviour, then set the insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces
to false
in the manipulation settings.
The library has been upgraded to TypeScript 2.8.
To celebrate there is a new sourceFile.organizeImports()
method. It takes a long time
to run it on an entire code base though... (depends how big your project is, of course). At least it's still faster than a human :)
There were some "Base" declarations (ex. ClassDeclarationBase
) variables that were previously exported
from the library. It was previously necessary to export these because the TypeScript compiler would complain
about them needing to be public, but I manged to get around this by writing a script that modifies the declaration file to hide them after the fact.
The library is smarter about populating directories. See #285, #286, #287 for more details.
Improved the wrapping of non-node wrapped compiler objects (symbols, types, diagnostics, etc.). Previously symbols had to be compared like so:
someSymbol.equals(otherSymbol); // deprecated method
But now, as expected, you can compare them using an equality check:
someSymbol === otherSymbol
Class declaration was changed to be more like the compiler. Read #266 for more details.
All file system copies, moves, and deletes are now deffered until .save()
is called on the main ast
object.
For example:
import Project from "ts-morph";
const project = new Project();
// ...lots of code here that manipulates, copies, moves, and deletes files...
sourceFile.delete();
directory.delete();
otherSourceFile.move("OtherFile.ts");
otherSourceFile.copy("NewFile.ts");
// ...more code that changes files...
// copies, moves, deletes, and other changes are executed on the file system on this line
project.save();
This was done so that nothing will be passed to the file system until you are all done manipulating.
If you want the previous behaviour, then use the "Immediately" methods:
await sourceFile.copyImmediately("NewFile.ts"); // or use 'Sync' alternatives
await sourceFile.moveImmediately("NewFile2.ts");
await sourceFile.deleteImmediately();
await directory.deleteImmediately();
Moving directories is going to come soon. Follow the progress in #256.
The TypeScript peer dependency has been dropped, but there should be no loss of functionality! If there is, please open an issue.
The TypeScript compiler object used by this library can now be accessed via the ts
named export. Also non-node TypeScript compiler objects used in the public API of this library are now exported directly as named exports:
import Project, {ts, SyntaxKind, ScriptTarget} from "ts-morph";
If you were previously using both like so:
import * as ts from "typescript";
import Project from "ts-morph";
// ... other code
const tsSourceFile = ts.createSourceFile(...etc...);
const classDeclarations = sourceFile.getDescendantsByKind(ts.SyntaxKind.ClassDeclaration);
Then you should remove the dependency on the TypeScript compiler and change this code to only use a single import declaration:
import Project, {SyntaxKind, ts} from "ts-morph";
// ... other code
const tsSourceFile = ts.createSourceFile(...etc...);
const classDeclarations = sourceFile.getDescendantsByKind(SyntaxKind.ClassDeclaration);