Skip to content

Commit 7588831

Browse files
committed
Support non-ascii (unicode) characters in service name, operation name and parameter name.
This replaces regexp patterns that only worked with ascii characters with more proper matching that supports unicode identifiers in typescript/javascript. The platform must support "unicode-aware mode" (the u flag) for this to work. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode
1 parent 308a180 commit 7588831

13 files changed

+172
-29
lines changed

src/openApi/v2/parser/getOperationName.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import camelCase from 'camelcase';
22

3+
import sanitizeOperationName from '../../../utils/sanitizeOperationName';
4+
35
/**
46
* Convert the input value to a correct operation (method) classname.
57
* This will use the operation ID - if available - and otherwise fallback
68
* on a generated name from the URL
79
*/
810
export const getOperationName = (url: string, method: string, operationId?: string): string => {
911
if (operationId) {
10-
return camelCase(
11-
operationId
12-
.replace(/^[^a-zA-Z]+/g, '')
13-
.replace(/[^\w\-]+/g, '-')
14-
.trim()
15-
);
12+
return camelCase(sanitizeOperationName(operationId).trim());
1613
}
1714

1815
const urlWithoutPlaceholders = url
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import camelCase from 'camelcase';
22

33
import { reservedWords } from '../../../utils/reservedWords';
4+
import sanitizeOperationParameterName from '../../../utils/sanitizeOperationParameterName';
45

56
/**
67
* Replaces any invalid characters from a parameter name.
78
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
89
*/
910
export const getOperationParameterName = (value: string): string => {
10-
const clean = value
11-
.replace(/^[^a-zA-Z]+/g, '')
12-
.replace(/[^\w\-]+/g, '-')
13-
.trim();
11+
const clean = sanitizeOperationParameterName(value).trim();
1412
return camelCase(clean).replace(reservedWords, '_$1');
1513
};
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import camelCase from 'camelcase';
22

3+
import sanitizeServiceName from '../../../utils/sanitizeServiceName';
4+
35
/**
46
* Convert the input value to a correct service name. This converts
57
* the input string to PascalCase.
68
*/
79
export const getServiceName = (value: string): string => {
8-
const clean = value
9-
.replace(/^[^a-zA-Z]+/g, '')
10-
.replace(/[^\w\-]+/g, '-')
11-
.trim();
10+
const clean = sanitizeServiceName(value).trim();
1211
return camelCase(clean, { pascalCase: true });
1312
};

src/openApi/v3/parser/getOperationName.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import camelCase from 'camelcase';
22

3+
import sanitizeOperationName from '../../../utils/sanitizeOperationName';
4+
35
/**
46
* Convert the input value to a correct operation (method) classname.
57
* This will use the operation ID - if available - and otherwise fallback
68
* on a generated name from the URL
79
*/
810
export const getOperationName = (url: string, method: string, operationId?: string): string => {
911
if (operationId) {
10-
return camelCase(
11-
operationId
12-
.replace(/^[^a-zA-Z]+/g, '')
13-
.replace(/[^\w\-]+/g, '-')
14-
.trim()
15-
);
12+
return camelCase(sanitizeOperationName(operationId).trim());
1613
}
1714

1815
const urlWithoutPlaceholders = url
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import camelCase from 'camelcase';
22

33
import { reservedWords } from '../../../utils/reservedWords';
4+
import sanitizeOperationParameterName from '../../../utils/sanitizeOperationParameterName';
45

56
/**
67
* Replaces any invalid characters from a parameter name.
78
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
89
*/
910
export const getOperationParameterName = (value: string): string => {
10-
const clean = value
11-
.replace(/^[^a-zA-Z]+/g, '')
12-
.replace('[]', 'Array')
13-
.replace(/[^\w\-]+/g, '-')
14-
.trim();
11+
const clean = sanitizeOperationParameterName(value).trim();
1512
return camelCase(clean).replace(reservedWords, '_$1');
1613
};

src/openApi/v3/parser/getServiceName.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ describe('getServiceName', () => {
99
expect(getServiceName('@fooBar')).toEqual('FooBar');
1010
expect(getServiceName('$fooBar')).toEqual('FooBar');
1111
expect(getServiceName('123fooBar')).toEqual('FooBar');
12+
expect(getServiceName('non-ascii-æøåÆØÅöôêÊ字符串')).toEqual('NonAsciiÆøåÆøÅöôêÊ字符串');
1213
});
1314
});
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import camelCase from 'camelcase';
22

3+
import sanitizeServiceName from '../../../utils/sanitizeServiceName';
4+
35
/**
46
* Convert the input value to a correct service name. This converts
57
* the input string to PascalCase.
68
*/
79
export const getServiceName = (value: string): string => {
8-
const clean = value
9-
.replace(/^[^a-zA-Z]+/g, '')
10-
.replace(/[^\w\-]+/g, '-')
11-
.trim();
10+
const clean = sanitizeServiceName(value).trim();
1211
return camelCase(clean, { pascalCase: true });
1312
};

src/utils/sanitizeOperationName.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import sanitizeServiceName from './sanitizeServiceName';
2+
3+
/**
4+
* sanitizeOperationName does the same as sanitizeServiceName.
5+
*/
6+
const sanitizeOperationName = sanitizeServiceName;
7+
export default sanitizeOperationName;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import sanitizeOperationName from './sanitizeOperationName';
2+
3+
const sanitizeOperationParameterName = (name: string): string => {
4+
const withoutBrackets = name.replace('[]', 'Array');
5+
return sanitizeOperationName(withoutBrackets);
6+
};
7+
export default sanitizeOperationParameterName;

src/utils/sanitizeServiceName.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Sanitizes service names, so they are valid typescript identifiers of a certain form.
3+
*
4+
* 1: Remove any leading characters that are illegal as starting character of a typescript identifier.
5+
* 2: Replace illegal characters in remaining part of type name with underscore (-).
6+
*
7+
* Step 1 should perhaps instead also replace illegal characters with underscore, or prefix with it, like sanitizeEnumName
8+
* does. The way this is now one could perhaps end up removing all characters, if all are illegal start characters. It
9+
* would be sort of a breaking change to do so, though, previously generated code might change then.
10+
*
11+
* Javascript identifier regexp pattern retrieved from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers
12+
*
13+
* The output of this is expected to be converted to PascalCase
14+
*/
15+
const sanitizeServiceName = (name: string) =>
16+
name.replace(/^[^\p{ID_Start}]+/u, '').replace(/[^$\u200c\u200d\p{ID_Continue}]/gu, '-');
17+
18+
export default sanitizeServiceName;

0 commit comments

Comments
 (0)