Ts-morph: Adding nodes to an ArrayLiteralExpression

Created on 26 Aug 2017  路  4Comments  路  Source: dsherret/ts-morph

I located a node of kind 'ArrayLiteralExpression' via a getFirstChildByKindOrThrow(SyntaxKind.ArrayLiteralExpression) call.

How can I add additional items to the array? I cannot find an interface from ts-simple-ast that represents this array expression.

The actual code I'd like to manipulate is:

@NgModule({
  declarations: [
    AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule
  , WchNgModule.forRoot(environment)],
  providers: [],
  entryComponents: [...LAYOUTS],
bootstrap: [AppComponent]
})

I'd like to add a value to the 'decorations' property (and managed to locate the array node).

manipulation navigation

Most helpful comment

@CarstenLeue, this is implemented in 0.57.0. I'll add documentation later.


Nodes of kind ts.SyntaxKind.ArrayLiteralExpressions are now ArrayLiteralExpression. So in the example above, you can do the following assertion (note that when #36 is implemented, the assertion would not be necessary):

const array = declarationsProp.getFirstChildByKindOrThrow(SyntaxKind.ArrayLiteralExpression) as ArrayLiteralExpression;

From there, you can do any of the following:

array.addElement("1"); // [1]
array.addElements(["5", "6"]); // [1, 5, 6]
array.insertElement(1, "2"); // [1, 2, 5, 6]
array.insertElements(2, ["3", "4"]); // [1, 2, 3, 4, 5, 6]
array.removeElement(5); // [1, 2, 3, 4, 5]
array.removeElement(array.getElements()[0]); // [2, 3, 4, 5]

Let me know if you run into any issues.

All 4 comments

Unfortunately, this has not been implemented yet. Right now I'm focusing on getting the higher level concepts implemented (like removing classes, enums, etc.)

Based on this issue, I've opened up #63 which will make it easier to insert or replace text in a source file for features that are not implemented (like this one). That way there will at least always be a temporary solution for manipulation tasks.

Thanks for your answer. I'll have a look at #63 .

Let me point our that your library is outstanding, so much simpler to use than the raw AST.
Please keep up with the good work!

Thanks for the compliment! :) Yeah, I've pulled out my hair many times trying to figure out the raw AST. Let me know if you run into any issues using the library.

Take note that #63 is implemented and available in 0.56.1. It will be slightly annoying to use because it will blow away the previously navigated nodes, but it should be an ok solution for the time being.

I believe the code would be something like (untested):

// just realized I don't have a convenient .getDecorator("NgModule") method... will add in #59
const decorator = classDeclaration.getDecorators().find(d => d.getName() === "NgModule")!;
const arg = decorator.getArguments()[0];

// this part is not so nice because there's not an easy way of navigating
// ObjectLiteralExpressions right now... I opened #65 for that
const declarationsProp = arg.getDescendants()
    .find(d => d.getKind() === SyntaxKind.PropertyAssignment &&
        (d.compilerNode as ts.PropertyAssignment).name === "declarations");
const array = declarationsProp.getFirstChildByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
const closeBracketToken = array.getLastChildByKindOrThrow(SyntaxKind.CloseBracketToken);

sourceFile.insertText(closeBracketToken.getPos(), `, "something new!"`);

So after doing this everything you've just navigated to won't work... for example, doing anything on classDeclaration, decorator, arg, declarationsProp, etc.. will throw an error because these nodes were blown away by the sourceFile.insertText(...). You would have to renavigate to them: classDeclaration = sourceFile.getClass("YourClassName")!

That's an unfortunate side effect of using it and why it would be nice if there were actual methods for doing this.


All that said, I'm going to take a break from working on removing nodes and prioritize this because it's quite easy to implement. I'll start work on it tomorrow evening.

@CarstenLeue, this is implemented in 0.57.0. I'll add documentation later.


Nodes of kind ts.SyntaxKind.ArrayLiteralExpressions are now ArrayLiteralExpression. So in the example above, you can do the following assertion (note that when #36 is implemented, the assertion would not be necessary):

const array = declarationsProp.getFirstChildByKindOrThrow(SyntaxKind.ArrayLiteralExpression) as ArrayLiteralExpression;

From there, you can do any of the following:

array.addElement("1"); // [1]
array.addElements(["5", "6"]); // [1, 5, 6]
array.insertElement(1, "2"); // [1, 2, 5, 6]
array.insertElements(2, ["3", "4"]); // [1, 2, 3, 4, 5, 6]
array.removeElement(5); // [1, 2, 3, 4, 5]
array.removeElement(array.getElements()[0]); // [2, 3, 4, 5]

Let me know if you run into any issues.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

claudv picture claudv  路  3Comments

rulatir picture rulatir  路  3Comments

allenhwkim picture allenhwkim  路  4Comments

HamedFathi picture HamedFathi  路  3Comments

donaldpipowitch picture donaldpipowitch  路  5Comments