Skip to content

Commit

Permalink
Add new JS.ScopedVariableDeclarations for variables (#122)
Browse files Browse the repository at this point in the history
* Add new `JS.ScopedVariableDeclarations` for variables

JS/TS variables are typically introduced by a `let`, `const`, or `var` keyword (not for parameters or fields, however). This and the fact that for multi-variable declarations each variable can declare its own type (unlike in Java), requires us to add a JS-specific construct.

* Some changes went missing?

* Add `J.VariableDeclarations`
  • Loading branch information
knutwannheden authored Oct 4, 2024
1 parent 4bd243b commit 0b94b8d
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 24 deletions.
28 changes: 19 additions & 9 deletions openrewrite/src/javascript/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,7 @@ export class JavaScriptParserVisitor {
visitVariableDeclaration(node: ts.VariableDeclaration) {
return new J.VariableDeclarations.NamedVariable(
randomId(),
this.prefix(node),
Space.EMPTY,
Markers.EMPTY,
this.visit(node.name),
[],
Expand All @@ -1379,17 +1379,27 @@ export class JavaScriptParserVisitor {
}

visitVariableDeclarationList(node: ts.VariableDeclarationList) {
return new J.VariableDeclarations(
const kind = node.getFirstToken(this.sourceFile);
return new JS.ScopedVariableDeclarations(
randomId(),
this.prefix(node),
Markers.EMPTY,
[],
this.mapModifiers(node),
node.declarations[0].type ? this.visit(node.declarations[0].type) : null,
null,
[],
this.rightPaddedList([...node.declarations], this.suffix)
);
kind?.kind === ts.SyntaxKind.LetKeyword ? JS.ScopedVariableDeclarations.Scope.Let :
kind?.kind === ts.SyntaxKind.ConstKeyword ? JS.ScopedVariableDeclarations.Scope.Const : JS.ScopedVariableDeclarations.Scope.Var,
node.declarations.map(declaration => {
return this.rightPadded(new J.VariableDeclarations(
randomId(),
this.prefix(declaration),
Markers.EMPTY,
[], // FIXME decorators?
[], // FIXME modifiers?
declaration.type ? this.visit(declaration.type) : null,
null, // FIXME varargs
[],
[this.rightPadded(this.visit(declaration), Space.EMPTY)]
), this.suffix(declaration));
})
)
}

visitFunctionDeclaration(node: ts.FunctionDeclaration) {
Expand Down
2 changes: 2 additions & 0 deletions openrewrite/src/javascript/tree/support_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export namespace JsSpace {
OBJECT_BINDING_DECLARATIONS_BINDING_PREFIX,
OBJECT_BINDING_DECLARATIONS_PREFIX,
PROPERTY_ASSIGNMENT_PREFIX,
SCOPED_VARIABLE_DECLARATIONS_PREFIX,
TEMPLATE_EXPRESSION_PREFIX,
TEMPLATE_EXPRESSION_VALUE_AFTER,
TEMPLATE_EXPRESSION_VALUE_PREFIX,
Expand Down Expand Up @@ -234,6 +235,7 @@ export namespace JsRightPadded {
JS_IMPORT_NAME,
OBJECT_BINDING_DECLARATIONS_BINDING_PROPERTY_NAME,
PROPERTY_ASSIGNMENT_NAME,
SCOPED_VARIABLE_DECLARATIONS_VARIABLES,
TEMPLATE_EXPRESSION_TAG,
UNION_TYPES,
}
Expand Down
89 changes: 89 additions & 0 deletions openrewrite/src/javascript/tree/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,95 @@ export class PropertyAssignment extends JSMixin(Object) implements Statement, Ty

}

@LstType("org.openrewrite.javascript.tree.JS$ScopedVariableDeclarations")
export class ScopedVariableDeclarations extends JSMixin(Object) implements Statement {
public constructor(id: UUID, prefix: Space, markers: Markers, scope: ScopedVariableDeclarations.Scope | null, variables: JRightPadded<Expression>[]) {
super();
this._id = id;
this._prefix = prefix;
this._markers = markers;
this._scope = scope;
this._variables = variables;
}

private readonly _id: UUID;

public get id(): UUID {
return this._id;
}

public withId(id: UUID): ScopedVariableDeclarations {
return id === this._id ? this : new ScopedVariableDeclarations(id, this._prefix, this._markers, this._scope, this._variables);
}

private readonly _prefix: Space;

public get prefix(): Space {
return this._prefix;
}

public withPrefix(prefix: Space): ScopedVariableDeclarations {
return prefix === this._prefix ? this : new ScopedVariableDeclarations(this._id, prefix, this._markers, this._scope, this._variables);
}

private readonly _markers: Markers;

public get markers(): Markers {
return this._markers;
}

public withMarkers(markers: Markers): ScopedVariableDeclarations {
return markers === this._markers ? this : new ScopedVariableDeclarations(this._id, this._prefix, markers, this._scope, this._variables);
}

private readonly _scope: ScopedVariableDeclarations.Scope | null;

public get scope(): ScopedVariableDeclarations.Scope | null {
return this._scope;
}

public withScope(scope: ScopedVariableDeclarations.Scope | null): ScopedVariableDeclarations {
return scope === this._scope ? this : new ScopedVariableDeclarations(this._id, this._prefix, this._markers, scope, this._variables);
}

private readonly _variables: JRightPadded<Expression>[];

public get variables(): Expression[] {
return JRightPadded.getElements(this._variables);
}

public withVariables(variables: Expression[]): ScopedVariableDeclarations {
return this.padding.withVariables(JRightPadded.withElements(this._variables, variables));
}

public acceptJavaScript<P>(v: JavaScriptVisitor<P>, p: P): J | null {
return v.visitScopedVariableDeclarations(this, p);
}

get padding() {
const t = this;
return new class {
public get variables(): JRightPadded<Expression>[] {
return t._variables;
}
public withVariables(variables: JRightPadded<Expression>[]): ScopedVariableDeclarations {
return t._variables === variables ? t : new ScopedVariableDeclarations(t._id, t._prefix, t._markers, t._scope, variables);
}
}
}

}

export namespace ScopedVariableDeclarations {
export enum Scope {
Const = 0,
Let = 1,
Var = 2,

}

}

@LstType("org.openrewrite.javascript.tree.JS$TemplateExpression")
export class TemplateExpression extends JSMixin(Object) implements Statement, Expression {
public constructor(id: UUID, prefix: Space, markers: Markers, delimiter: string, tag: JRightPadded<Expression> | null, strings: J[], _type: JavaType | null) {
Expand Down
15 changes: 14 additions & 1 deletion openrewrite/src/javascript/visitor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as extensions from "./extensions";
import {ListUtils, SourceFile, Tree, TreeVisitor} from "../core";
import {JS, isJavaScript, JsLeftPadded, JsRightPadded, JsContainer, JsSpace} from "./tree";
import {CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsBinary, ObjectBindingDeclarations, PropertyAssignment, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Void, Yield} from "./tree";
import {CompilationUnit, Alias, ArrowFunction, Await, DefaultType, Delete, Export, ExpressionStatement, FunctionType, JsImport, JsBinary, ObjectBindingDeclarations, PropertyAssignment, ScopedVariableDeclarations, StatementExpression, TemplateExpression, Tuple, TypeDeclaration, TypeOf, TypeOperator, Unary, Union, Void, Yield} from "./tree";
import {Expression, J, JContainer, JLeftPadded, JRightPadded, Space, Statement} from "../java/tree";
import {JavaVisitor} from "../java";
import * as Java from "../java/tree";
Expand Down Expand Up @@ -215,6 +215,19 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
return propertyAssignment;
}

public visitScopedVariableDeclarations(scopedVariableDeclarations: ScopedVariableDeclarations, p: P): J | null {
scopedVariableDeclarations = scopedVariableDeclarations.withPrefix(this.visitJsSpace(scopedVariableDeclarations.prefix, JsSpace.Location.SCOPED_VARIABLE_DECLARATIONS_PREFIX, p)!);
let tempStatement = this.visitStatement(scopedVariableDeclarations, p) as Statement;
if (!(tempStatement instanceof ScopedVariableDeclarations))
{
return tempStatement;
}
scopedVariableDeclarations = tempStatement as ScopedVariableDeclarations;
scopedVariableDeclarations = scopedVariableDeclarations.withMarkers(this.visitMarkers(scopedVariableDeclarations.markers, p));
scopedVariableDeclarations = scopedVariableDeclarations.padding.withVariables(ListUtils.map(scopedVariableDeclarations.padding.variables, el => this.visitJsRightPadded(el, JsRightPadded.Location.SCOPED_VARIABLE_DECLARATIONS_VARIABLES, p)));
return scopedVariableDeclarations;
}

public visitStatementExpression(statementExpression: StatementExpression, p: P): J | null {
statementExpression = statementExpression.withStatement(this.visitAndCast(statementExpression.statement, p)!);
return statementExpression;
Expand Down
33 changes: 19 additions & 14 deletions src/main/java/org/openrewrite/javascript/JavaScriptIsoVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public JS.ArrowFunction visitArrowFunction(JS.ArrowFunction arrowFunction, P p)
}

@Override
public J visitAwait(JS.Await await, P p) {
return super.visitAwait(await, p);
public JS.Await visitAwait(JS.Await await, P p) {
return (JS.Await) super.visitAwait(await, p);
}

@Override
Expand All @@ -64,8 +64,8 @@ public JS.Export visitExport(JS.Export export, P p) {
}

@Override
public J visitExpressionStatement(JS.ExpressionStatement statement, P p) {
return super.visitExpressionStatement(statement, p);
public JS.ExpressionStatement visitExpressionStatement(JS.ExpressionStatement statement, P p) {
return (JS.ExpressionStatement) super.visitExpressionStatement(statement, p);
}

@Override
Expand All @@ -89,13 +89,18 @@ public JS.ObjectBindingDeclarations visitObjectBindingDeclarations(JS.ObjectBind
}

@Override
public J visitPropertyAssignment(JS.PropertyAssignment propertyAssignment, P p) {
return super.visitPropertyAssignment(propertyAssignment, p);
public JS.PropertyAssignment visitPropertyAssignment(JS.PropertyAssignment propertyAssignment, P p) {
return (JS.PropertyAssignment) super.visitPropertyAssignment(propertyAssignment, p);
}

@Override
public J visitStatementExpression(JS.StatementExpression expression, P p) {
return super.visitStatementExpression(expression, p);
public JS.ScopedVariableDeclarations visitScopedVariableDeclarations(JS.ScopedVariableDeclarations scopedVariableDeclarations, P p) {
return (JS.ScopedVariableDeclarations) super.visitScopedVariableDeclarations(scopedVariableDeclarations, p);
}

@Override
public JS.StatementExpression visitStatementExpression(JS.StatementExpression expression, P p) {
return (JS.StatementExpression) super.visitStatementExpression(expression, p);
}

@Override
Expand All @@ -119,8 +124,8 @@ public JS.TypeDeclaration visitTypeDeclaration(JS.TypeDeclaration typeDeclaratio
}

@Override
public J visitTypeOf(JS.TypeOf typeOf, P p) {
return super.visitTypeOf(typeOf, p);
public JS.TypeOf visitTypeOf(JS.TypeOf typeOf, P p) {
return (JS.TypeOf) super.visitTypeOf(typeOf, p);
}

@Override
Expand All @@ -134,13 +139,13 @@ public JS.Union visitUnion(JS.Union union, P p) {
}

@Override
public J visitVoid(JS.Void aVoid, P p) {
return super.visitVoid(aVoid, p);
public JS.Void visitVoid(JS.Void aVoid, P p) {
return (JS.Void) super.visitVoid(aVoid, p);
}

@Override
public J visitYield(JS.Yield yield, P p) {
return super.visitYield(yield, p);
public JS.Yield visitYield(JS.Yield yield, P p) {
return (JS.Yield) super.visitYield(yield, p);
}

// J overrides.
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,20 @@ public J visitPropertyAssignment(JS.PropertyAssignment propertyAssignment, P p)
return pa;
}

public J visitScopedVariableDeclarations(JS.ScopedVariableDeclarations scopedVariableDeclarations, P p) {
JS.ScopedVariableDeclarations vd = scopedVariableDeclarations;
vd = vd.withPrefix(visitSpace(vd.getPrefix(), JsSpace.Location.SCOPED_VARIABLE_DECLARATIONS_PREFIX, p));
vd = vd.withMarkers(visitMarkers(vd.getMarkers(), p));
Statement temp = (Statement) visitStatement(vd, p);
if (!(temp instanceof JS.ScopedVariableDeclarations)) {
return temp;
} else {
vd = (JS.ScopedVariableDeclarations) temp;
}
vd = vd.getPadding().withVariables(ListUtils.map(vd.getPadding().getVariables(), e -> visitRightPadded(e, JsRightPadded.Location.SCOPED_VARIABLE_DECLARATIONS_VARIABLE, p)));
return vd;
}

public J visitStatementExpression(JS.StatementExpression expression, P p) {
JS.StatementExpression se = expression;
Expression temp = (Expression) visitExpression(se, p);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,30 @@ public J visitPropertyAssignment(JS.PropertyAssignment propertyAssignment, Print
return propertyAssignment;
}

@Override
public J visitScopedVariableDeclarations(JS.ScopedVariableDeclarations variableDeclarations, PrintOutputCapture<P> p) {
beforeSyntax(variableDeclarations, Space.Location.VARIABLE_DECLARATIONS_PREFIX, p);

if (variableDeclarations.getScope() != null) {
switch (variableDeclarations.getScope()) {
case Let:
p.append("let");
break;
case Const:
p.append("const");
break;
case Var:
p.append("var");
break;
}
}

visitRightPadded(variableDeclarations.getPadding().getVariables(), JsRightPadded.Location.SCOPED_VARIABLE_DECLARATIONS_VARIABLE, ",", p);

afterSyntax(variableDeclarations, p);
return variableDeclarations;
}

@Override
public J visitObjectBindingDeclarations(JS.ObjectBindingDeclarations objectBindingDeclarations, PrintOutputCapture<P> p) {
beforeSyntax(objectBindingDeclarations, Space.Location.VARIABLE_DECLARATIONS_PREFIX, p);
Expand Down Expand Up @@ -755,6 +779,8 @@ public J visitVariableDeclarations(J.VariableDeclarations multiVariable, PrintOu
public J visitVariable(J.VariableDeclarations.NamedVariable variable, PrintOutputCapture<P> p) {
beforeSyntax(variable, Space.Location.VARIABLE_PREFIX, p);
visit(variable.getName(), p);
JLeftPadded<Expression> initializer = variable.getPadding().getInitializer();
visitLeftPadded("=", initializer, JLeftPadded.Location.VARIABLE_INITIALIZER, p);
afterSyntax(variable, p);
return variable;
}
Expand Down
Loading

0 comments on commit 0b94b8d

Please sign in to comment.