Skip to content

jsii-rosetta: Escaped interpolation in Typescript doesn't convert well to Python and C# #3656

Description

@mutahhir

Describe the bug

Typescript template strings containing escaped ${} sections are not handled correctly in Python and C#.

Expected Behavior

The following Typescript code:

`\${Template with Terraform Escape sequence: ${str}!}`,

should result in python code of:

f"${{Template with Terraform Escape sequence: {str}!}}"

and C# code of:

$"${{Template with Terraform Escape sequence: {str}!}}",

Current Behavior

The following Typescript code:

    const str = "problematic!";
    return [
      `Simple Template without escapes`,
      `Simple Template with simple escapes: ${10}`,
      `\${Template with Terraform Escape sequence: ${str}!}`,
      `\$\{Template with more escaped Terraform Escape sequence: ${str}!\}`,
      `$\{Template with differently escaped Terraform Escape sequence: ${str}!\}`,
    ];

generates the following output:

[
  'Simple Template without escapes',
  'Simple Template with simple escapes: 10',
  '${Template with Terraform Escape sequence: problematic!!}',
  '${Template with more escaped Terraform Escape sequence: problematic!!}',
  '${Template with differently escaped Terraform Escape sequence: problematic!!}'
]

However, translation to other languages fares less than ideal results:

Python

        str = "problematic!"
        return ["Simple Template without escapes",
                f"Simple Template with simple escapes: {10}",
                f"\\${Template with Terraform Escape sequence: {str}!}",
                f"\\$\\{Template with more escaped Terraform Escape sequence: {str}!\\}",
                f"$\\{Template with differently escaped Terraform Escape sequence: {str}!\\}"
                ]```

### C#

```cs
        var str = "problematic!";
        return new[] { "Simple Template without escapes",
         $"Simple Template with simple escapes: {10}",
         $"\\${Template with Terraform Escape sequence: {str}!}",
         $"\\$\\{Template with more escaped Terraform Escape sequence: {str}!\\}",
         $"$\\{Template with differently escaped Terraform Escape sequence: {str}!\\}" };

Reproduction Steps

// src/index.ts
export interface GreeterProps {
  readonly greetee: string;
}

export class Greeter {
  public constructor() {}

  public greets(): string[] {
    const str = "problematic!";
    return [
      `Simple Template without escapes`,
      `Simple Template with simple escapes: ${10}`,
      `\${Template with Terraform Escape sequence: ${str}!}`,
      `\$\{Template with more escaped Terraform Escape sequence: ${str}!\}`,
      `$\{Template with differently escaped Terraform Escape sequence: ${str}!\}`,
    ];
  }
}
// converter.ts
import * as rosetta from "jsii-rosetta";
import * as fs from "fs/promises";
import * as path from "path";
import { Greeter } from "./src/index";

type File = { contents: string; fileName: string };

const translations = {
  python: (file: File) =>
    rosetta.translateTypeScript(file, new rosetta.PythonVisitor()).translation,
  csharp: (file: File) =>
    rosetta.translateTypeScript(file, new rosetta.CSharpVisitor()).translation,
};

export async function convert(
  language: "python" | "csharp",
  inPath: string,
  outPath: string
) {
  const translater = translations[language];

  if (!translater) {
    throw new Error("Unsupported language used: " + language);
  }
  const fullPath = path.resolve(inPath);
  const tsCode = await fs.readFile(fullPath, "utf-8");
  const code = translater({ fileName: "terraform.tf", contents: tsCode });

  fs.writeFile(path.resolve(outPath), code, "utf-8");
}

(async () => {
  console.log(new Greeter().greets());
  await fs.mkdir("dist", { recursive: true });
  convert("python", "src/index.ts", "dist/main.py");
  convert("csharp", "src/index.ts", "dist/main.cs");
})();

To run:
npx ts-node converter.ts

Possible Solution

No response

Additional Information/Context

No response

SDK version used

1.75.0

Environment details (OS name and version, etc.)

Mac OS 13.2 Arm64

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingp2

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions