[typescript] Prevent generating invalid enum code due to empty variable names (#20699)

* [typescript] Prevent generating invalid enum code due to empty variable names

After sanitizing all characters (e.g. multibyte characters), the enum variable name may become an empty string.
Since an empty string would cause a syntax error, this patch pads the pseudo variable name (`STRING`) to avoid that issue.

For example, given the following OpenAPI definition:

```yaml
openapi: "3.0.0"
info:
  title: Sample project
  version: '1.0'
  description: 'Sample API Check "API Key" '
  license:
    name: Apache 2.0
    url: 'https://www.apache.org/licenses/LICENSE-2.0'
paths: {}
components:
  schemas:
    Greeting:
      type: string
      enum:
        - 'こんにちは'
        - '你好'
        - '안녕하세요'
```

The current logic generates the following code for Greeting:

```typescript
export enum Greeting {
     = 'こんにちは',
    2 = '你好',
    3 = '안녕하세요'
}
```

This code is invalid. With this patch, the generated code becomes:

```typescript
export enum Greeting {
    STRING = 'こんにちは',
    STRING2 = '你好',
    STRING3 = '안녕하세요'
}
```

Signed-off-by: moznion <moznion@mail.moznion.net>

* Remove unnecessary imports

Signed-off-by: moznion <moznion@mail.moznion.net>

* Use new sanitizer for TypeScript symbol which takes wider variety characters for enum var name

Signed-off-by: moznion <moznion@mail.moznion.net>

---------

Signed-off-by: moznion <moznion@mail.moznion.net>
This commit is contained in:
moznion 2025-02-25 20:58:27 +09:00 committed by GitHub
parent a06a2b4c7b
commit 9537a7fb46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 2 deletions

View File

@ -311,9 +311,25 @@ public class TypeScriptClientCodegen extends AbstractTypeScriptClientCodegen imp
if (enumName.matches("\\d.*")) { // starts with number
return "_" + enumName;
} else {
return enumName;
}
if (enumName.isEmpty()) {
// After sanitizing *all* characters (e.g. multibyte characters), the var name becomes an empty string.
// An empty string would cause a syntax error, so this code attempts to re-sanitize the name using another sanitizer that allows a wider variety of characters.
// For backward compatibility, this additional sanitization is only applied if the original sanitized name is empty.
final String sanitized = sanitizeNameForTypeScriptSymbol(name);
if (sanitized.isEmpty()) {
// After re-sanitizing, this pads a pseudo var name ("STRING") if still the name is empty.
return "STRING";
}
return "_" + sanitized;
}
return enumName;
}
private String sanitizeNameForTypeScriptSymbol(String name) {
return sanitizeName(name, "[^\\p{L}\\p{Nd}\\$_]");
}
private String getNameWithEnumPropertyNaming(String name) {

View File

@ -5,6 +5,7 @@ import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.media.*;
import org.openapitools.codegen.*;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.openapitools.codegen.languages.TypeScriptClientCodegen;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
@ -12,6 +13,9 @@ import org.openapitools.codegen.utils.ModelUtils;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -179,4 +183,31 @@ public class TypeScriptClientCodegenTest {
Assert.assertEquals(codegen.getLicenseName(), licenseName);
}
@Test
public void testForAllSanitizedEnum() throws Exception {
final File output = Files.createTempDirectory("typescriptnodeclient_").toFile();
output.deleteOnExit();
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("typescript")
.setInputSpec("src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml")
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
final ClientOptInput clientOptInput = configurator.toClientOptInput();
final DefaultGenerator generator = new DefaultGenerator();
final List<File> files = generator.opts(clientOptInput).generate();
files.forEach(File::deleteOnExit);
TestUtils.assertFileContains(
Paths.get(output + "/models/Greeting.ts"),
"export enum Greeting {\n" +
" _こんにちは = 'こんにちは',\n" +
" _你好 = '你好',\n" +
" _안녕하세요 = '안녕하세요',\n" +
" STRING = '!@#%',\n" +
" STRING2 = '^&*\uD83C\uDF63'",
"}"
);
}
}

View File

@ -0,0 +1,19 @@
openapi: "3.0.0"
info:
title: Sample project
version: '1.0'
description: 'Sample API Check "API Key" '
license:
name: Apache 2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0'
paths: {}
components:
schemas:
Greeting:
type: string
enum:
- 'こんにちは'
- '你好'
- '안녕하세요'
- '!@#%'
- '^&*🍣'