dsherret / ts-morph Goto Github PK
View Code? Open in Web Editor NEWTypeScript Compiler API wrapper for static analysis and programmatic code changes.
Home Page: https://ts-morph.com
License: MIT License
TypeScript Compiler API wrapper for static analysis and programmatic code changes.
Home Page: https://ts-morph.com
License: MIT License
This library is based on a lot of assumptions about the TypeScript library.
Take the following example property:
prop: string;
In certain parts of this library, no compile error would be thrown if the above type were changed to:
prop: string | number;
It would be good to have automated tests to make sure certain parts of the TypeScript compiler haven't changed. This could be done using this library and it would be very easy to do.
Is there a function like addEnum
for variables and constants?
If not, I want investigate and make it, but asking before
This would be easy to implement:
ast.addSourceFileFromStructure("fileName.ts", {
classes: [{
name: "MyClass",
// etc...
}],
// etc...
});
I've noticed a lot of problems with the current implementation for manipulation. It would be better to create specific functions for inserting and removing parts of the code. These could then be easily fine-tuned for performance.
Some ideas:
insertIntoSyntaxList
- Specify the index being inserted at.insertCreatingSyntaxList
removeFromSyntaxList
- Would know to remove the syntax list if necessaryThis would make manipulation much more reliable because then certain assumptions could be made about what is being inserted and removed. That would help a lot with performance and reduce the chance of error.
It would be better if there was some intelligence behind adding a method/property to a class (ex. private properties get inserted with other private properties). Currently, it just inserts to the end index.
Edit: Also, set accessors should be added after get accessors.
It would be useful to throw an error when someone tries to use a node that was removed from a source file. It would help catch some potential bugs.
If someone wants information about this node before it's removed, then they should create an object that stores the data they need before they remove it from a source file.
I'm going to need to fix the current code in order to do this. What I'm thinking is this...
When building an object like an enum:
enum MyEnum {}
)const enum MyEnum {}
)I need to try to preserve the existing nodes whenever making changes so I can't just parse the file each time. Something similar to the IncrementalParser inside the compiler would be nice.
Throwing an idea out there. I might have to do this:
That feel like that would be extremely reliable, but probably kind of slow. I guess get it working for now then do optimizations later.
Hi,
First, let' say ๐ about this library, very wonderful work !!
I am the maintainer of compodoc, a documentation tool for Angular applications. https://github.com/compodoc/compodoc
I need for some use-case to support dynamic imports of different things.
// module-a.ts
export const VERSION = 5;
// module-b.ts
import { VERSION } from './module-a';
let toto = VERSION;
console.log(toto);
Would be nice if during AST parsing with TypeScript, if i could edit the AST node of toto variable to inject VERSION instead of just having a reference.
Did you know how can i achieve this with your lib or just with TypeScript compiler APIs ?
This blog http://blog.scottlogic.com/2017/05/02/typescript-compiler-api-revisited.html, chapter "TRANSFORMING THE AST" helps a little
Thanks
This would be part of ensureStructuresMatchMixins.
Just need to have an automated way of verifying a structure doesn't have a mixin structure that it doesn't support.
For example:
const node = createWrappedNode(node, { typeChecker }) as FunctionDeclaration;
node.getReturnType(); // would work... currently it throws
ast.addSourceFilesFromTsConfig(tsconfigPath?: string)
I have a line in a class in a file I'm processing like this:
public static readonly Version: string = "1.0";
I get via:
ClassDeclaration.getStaticProperties
and I want to change it to
public static readonly Version: string = "2.0";
The call I make is
setInitializer("\"2.0\"");
But it blows up with:
Code:
public static readonly Version: string; = "2.0" // version to pass to EDAProperties
at BaseError (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/errors/BaseError.js:5:9)
at InvalidOperationError (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/errors/InvalidOperationError.js:6:9)
at handleNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/manipulation/insertStraight.js:28:19)
at handleNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/manipulation/insertStraight.js:37:13)
at handleNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/manipulation/insertStraight.js:37:13)
at handleNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/manipulation/insertStraight.js:37:13)
at handleNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/manipulation/insertStraight.js:37:13)
at handleNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/manipulation/insertStraight.js:37:13)
at handleNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/manipulation/insertStraight.js:37:13)
at handleNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/manipulation/insertStraight.js:37:13)
setInitializer first removes the existing initializer bringing the semi-colon all the way over, then adds the new initializer which ends-up being after the semi-colon.
There's some constants that are lazily stored in the language service class (like newline char and string char). It would be good to move these outside the language service into its own class and then expose this to users so they can set the options for manipulation.
This is also a refactoring task. The factory knows too much right now.
Passing around source files helps improve the performance of the application because when a source file is required, it won't have to go up the parents of the tree to find the root. Providing a source file is optional though.
It would be good to create a tslint rule for this project that requires function calls provide a source file if the parameter expects one.
I'm not sure that's the right issue name.
I have a class with two static class members and I want to be able to swap their order in the source file. I've poked around a bit and can't find a way to do so (I can identify when they're out of order, but can't change the fact).
Would be good to add some helper functions and improve manipulation for this.
Add navigation methods:
getOverloads()
- returns the overload declarationsgetImplementation()
- returns implementation declaration fromisImplementation()
-- does .hasBody() basicallyAdd manipulation methods (these probably have to be specific to the implementation):
func.addOverload({})
func.addOverloads([{}, {}])
func.insertOverload(0, {})
func.insertOverloads(0, [{}, {}])
method.addOverload({})
method.addOverloads([{}, {}])
method.insertOverload(0, {})
method.insertOverloads(0, [{}, {}])
ctor.addOverload({})
ctor.addOverloads([{}, {}])
ctor.insertOverload(0, {})
ctor.insertOverloads(0, [{}, {}])
It would be better to only create one of each of these that the user can use, then swap it out internally with a new one when manipulation is done.
This will be useful because then people can do something like a rename refactor, get all the changed files, then save them to the disk.
Add:
TsSimpleAst.getUnsavedSourceFiles()
TsSimpleAst.saveUnsavedSourceFiles()
TsSimpleAst.saveUnsavedSourceFilesSync()
Before continuing, it would be good to figure out how to implement types. Should be easy... post some notes in here if necessary.
It should mirror the typescript compiler a bit more.
Also:
findReferences()
function to Identifier
.getChildAtPos
& getDescendantAtPos
)Edit (2018/09/16): This was changed from .fill
to .set
for version 15.
Basically, this would allow you to set multiple properties at once:
functionDeclaration.set({
isAsync: true,
hasExportKeyword: true
});
It would be useful with #45 too:
functionDeclaration2.set(functionDeclaration1.getStructure());
Edit: Mostly all done... still todo:
I've been documenting the code using JSDocs, but I also make heavy use of mixins... I'm not sure if any JSDoc generators will be able to understand what I'm doing with the mixins.
Should be simple. Just need to add support for statements like:
export {identifier};
export default identifier
does not need to be supported at the current time because that's an export assignment.
As title.
Probably just add a hasModuleKeyword()
and hasNamespaceKeyword()
method. This isn't as useful as it used to be and I don't think it's worth it to introduce a NamespaceDeclarationType
enum.
Parameter decorators need to go on a single line.
I'm going to use this issue to collect together some low priority manipulation tasks. I'll come back to implement these at the end.
ClassDeclaration
Decorator
JSDoc
Function/Methods/Constructors
VariableDeclarationList
VariableStatement
More to come...
Need support for export declarations...
export * from "./file";
export {name} from "./file";
I have a ClassDeclaration, classDec, for the following class:
class MyClass {
public x: number = 0;
}
If I do something like:
classDec.getInstanceMembers()
.filter(m => m.hasModifier(ts.SyntaxKind.PublicKeyword))
.forEach(member => {
if (member instanceof ParameterDeclaration || member instanceof PropertyDeclaration) {
let mType = member.getType();
...
}
});
It blows-up because if something to do with an undefined symbol.
Here's the stack trace:
TypeError: Cannot read property 'flags' of undefined
at getCheckFlags (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/node_modules/typescript/lib/typescript.js:26031:26)
at getTypeOfSymbol (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/node_modules/typescript/lib/typescript.js:29477:17)
at getTypeOfNode (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/node_modules/typescript/lib/typescript.js:45506:24)
at Object.getTypeAtLocation (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/node_modules/typescript/lib/typescript.js:25507:31)
at TypeChecker.getTypeAtLocation (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/compiler/tools/TypeChecker.js:51:62)
at PropertyDeclaration.getType (/Users/mhaan/AppDev/tscompile/node_modules/ts-simple-ast/dist/compiler/base/TypedNode.js:8:50)
at ast.getSourceFiles.forEach.processNS.forEach.classDec.getInstanceMembers.filter.forEach.member (/Users/mhaan/AppDev/tscompile/lib/app2.js:46:36)
at Array.forEach (native)
Note that if I exclude PropertyDeclarations (by removing "|| member instanceof PropertyDeclaration") then that member is not found at all.
It would be useful to have the ability to navigate source files without having a language service, program, etc...
Use case?: This would be useful for people who purely want to navigate source files without manipulating them. It would use less memory and be faster.
I'm getting about 90 of these errors, all originating from this library in node_modules. I don't know why they're being generated (tsc shouldn't be tying to compile anything in node_modules) but I do note that I can't seem to find a "Constructor" defined anywhere in the library - so I don't know what's going on.
Add extends and implements to ClassDeclaration.
Working within the library, I might not notice when an error is happening in the generated definition files. It would be good to add a test that checks to make sure there are no errors.
See #32.
Add the ability to remove files from the main AST object.
For example: .getProperty("nameOfProperty")
This will return the implementation declaration or the first signature if in an ambient context.
Instead of creating a new TypeChecker when getting information about a type, it would be best to just create it from the checker property if it exists.
I think? Would be good to look into this.
The base for this already exists in createGetStructureFunctions
; however, that should be changed to remove scenario specific logic. The user should be able to do the following:
const functionStructure = functionDeclaration.getStructure();
// EDIT: Won't be doing this below. People can modify the returned object when
// they're done themselves.
// or... (return type would be a partial of FunctionDeclarationStructure)
const functionStructure = functionDeclaration.getStructure({
include: [nameof(AsyncableNode), nameof(GeneratorableNode)]
});
// or... (return type would be a partial of FunctionDeclarationStructure)
const functionStructure = functionDeclaration.getStructure({
exclude: [nameof(AsyncableNode), nameof(GeneratorableNode)]
});
This seems pretty tedious to implement and susceptible to bugs, but these problems can basically be eliminated with code generation.
Left to do (does not include already completed tasks or all tasks):
Should some heavy method call's result be memoized and then the caches wiped away after manipulation has occurred?
Would be nice to be able to do this:
const sourceFiles = ast.getSourceFiles("src/compiler/**/*.ts");
Consider holding a reference to the root source file in each node.
Currently, removing nodes is not very well supported in this library. Will add a list of everything that needs to be implemented soon.
While working on this I'm going to:
This should be done using code generation. It's easy to forget to add methods for these.
Edit: I'm having second thoughts about this because it would spam the auto-complete. I'm thinking it might be better to put this on a utils class.
import Ast, {TypeGuards} from "ts-simple-ast";
// ...
if (TypeGuards.isEnumDeclaration(node)) {
// node is EnumDeclaration in here
}
if (TypeGuards.isBodiedNode(node)) {
// node is Node & BodiedNode in here
}
This class should be completely code generated so that no unit tests are required (code generation is low risk for mistakes) and so that it doesn't need to be maintained (because maintaining it would be a pain!)
Right now the code for replacing one underlying source file could possibly have a memory leak if a mistake were made. It would be good to add some tests for some of the manipulation (especially ones that create temporary nodes in the process) to ensure that they don't remain in the cache.
Probably a simple test is sufficient... it could do a few different kinds of manipulations and then manipulate back to the original code. Once done it could ensure that the size of the cache is the same as it was when it started.
To be clear, it's an overloaded constructor:
constructor();
constructor(Email: string, FirstName: string, LastName: string, SecurityToken: string);
constructor(public Email: string = "", public FirstName?: string, public LastName?: string, public SecurityToken?: string) {
super();
}
Neither getInstanceMembers nor getInstanceProperties returns any of those.
Before continuing, it would be good to figure out how to implement symbols. Post some notes in here if necessary.
Trying to use this lib in a TypeScript project of my own and having difficulty taking advantage of the typings. For example, the following code works (it's just something i'm hacking at right now):
import Ast from "ts-simple-ast";
// import { NamespaceDeclaration } from "ts-simple-ast";
import * as ts from "typescript";
// import * as _ from "lodash";
const ast = new Ast({
tsConfigFilePath: "tsconfig.json"
});
ast.addSourceFiles("msgs/**/*{.d.ts,.ts}");
ast.getSourceFiles().forEach((sf, idx) => {
if (idx === 0) {
console.log(sf.getFilePath());
sf.getNamespaces().forEach(ns => processNS([], ns));
console.log("=================");
}
});
function processNS(nsParents, ns) {
let currNSStr = nsParents.length === 0 ? ns.node.name.text : nsParents.map(ns => ns.node.name.text).join(".");
ns.getClasses().forEach(c => {
console.log(`${currNSStr}.${c.node.name.text}`);
});
let currNS = nsParents.concat(ns);
ns.getChildrenOfKind(ts.SyntaxKind.ModuleDeclaration).forEach(childNS => {
processNS(currNS, childNS);
});
}
The problem is I'd like, for example, for the function to be written as follows:
function processNS(nsParents, ns: NamespaceDeclaration) {
let currNSStr = nsParents.length === 0 ? ns.node.name.text : nsParents.map(ns => ns.node.name.text).join(".");
ns.getClasses().forEach(c => {
console.log(`${currNSStr}.${c.node.name.text}`);
});
let currNS = nsParents.concat(ns);
ns.getChildrenOfKind(ts.SyntaxKind.ModuleDeclaration).forEach(childNS => {
processNS(currNS, childNS);
});
}
But then vscode complains about the "node" portion of "ns.node.name.text" and "c.node.name.text" (and it also complains about "childNS" which it says is of type Node<ts.Node>).
If I change it instead to:
function processNS(nsParents, ns: ts.NamespaceDeclaration) {
let currNSStr = nsParents.length === 0 ? ns.node.name.text : nsParents.map(ns => ns.node.name.text).join(".");
ns.getClasses().forEach(c => {
console.log(`${currNSStr}.${c.node.name.text}`);
});
let currNS = nsParents.concat(ns);
ns.getChildrenOfKind(ts.SyntaxKind.ModuleDeclaration).forEach(childNS => {
processNS(currNS, childNS);
});
}
Then it also complains about the "node" in "ns.node.name.text" but also "getClasses" of "ns.getClasses()" and "getChildrenOfKind" of "ns.getChildrenOfKind().
So, the question is - how should I be setting that type such that I can take advantage of the typing system?
SourceFile.getClasses() does not return classes which are inside a namespace. Do I need to do something to access the namespaces? I can get them with getNamespaces, but then I don't seem to be able to access the classes within that namespace...
Forgot to implement this for navigation.
Maybe this is unavoidable, but as the documentation is under construction not having these appear in the IDE is problematic.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.