Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non ascii character support #2040

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/openApi/v2/parser/escapeName.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import validTypescriptIdentifierRegex from '../../../utils/validTypescriptIdentifierRegex';

export const escapeName = (value: string): string => {
if (value || value === '') {
const validName = /^[a-zA-Z_$][\w$]+$/g.test(value);
const validName = validTypescriptIdentifierRegex.test(value);
if (!validName) {
return `'${value}'`;
}
Expand Down
7 changes: 2 additions & 5 deletions src/openApi/v2/parser/getEnum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Enum } from '../../../client/interfaces/Enum';
import sanitizeEnumName from '../../../utils/sanitizeEnumName';

export const getEnum = (values?: (string | number)[]): Enum[] => {
if (Array.isArray(values)) {
Expand All @@ -19,11 +20,7 @@ export const getEnum = (values?: (string | number)[]): Enum[] => {
};
}
return {
name: String(value)
.replace(/\W+/g, '_')
.replace(/^(\d+)/g, '_$1')
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
.toUpperCase(),
name: sanitizeEnumName(String(value)),
value: `'${value.replace(/'/g, "\\'")}'`,
type: 'string',
description: null,
Expand Down
9 changes: 3 additions & 6 deletions src/openApi/v2/parser/getOperationName.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import camelCase from 'camelcase';

import sanitizeOperationName from '../../../utils/sanitizeOperationName';

/**
* Convert the input value to a correct operation (method) classname.
* This will use the operation ID - if available - and otherwise fallback
* on a generated name from the URL
*/
export const getOperationName = (url: string, method: string, operationId?: string): string => {
if (operationId) {
return camelCase(
operationId
.replace(/^[^a-zA-Z]+/g, '')
.replace(/[^\w\-]+/g, '-')
.trim()
);
return camelCase(sanitizeOperationName(operationId).trim());
}

const urlWithoutPlaceholders = url
Expand Down
6 changes: 2 additions & 4 deletions src/openApi/v2/parser/getOperationParameterName.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import camelCase from 'camelcase';

import { reservedWords } from '../../../utils/reservedWords';
import sanitizeOperationParameterName from '../../../utils/sanitizeOperationParameterName';

/**
* Replaces any invalid characters from a parameter name.
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
*/
export const getOperationParameterName = (value: string): string => {
const clean = value
.replace(/^[^a-zA-Z]+/g, '')
.replace(/[^\w\-]+/g, '-')
.trim();
const clean = sanitizeOperationParameterName(value).trim();
return camelCase(clean).replace(reservedWords, '_$1');
};
7 changes: 3 additions & 4 deletions src/openApi/v2/parser/getServiceName.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import camelCase from 'camelcase';

import sanitizeServiceName from '../../../utils/sanitizeServiceName';

/**
* Convert the input value to a correct service name. This converts
* the input string to PascalCase.
*/
export const getServiceName = (value: string): string => {
const clean = value
.replace(/^[^a-zA-Z]+/g, '')
.replace(/[^\w\-]+/g, '-')
.trim();
const clean = sanitizeServiceName(value).trim();
return camelCase(clean, { pascalCase: true });
};
5 changes: 2 additions & 3 deletions src/openApi/v2/parser/getType.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { Type } from '../../../client/interfaces/Type';
import sanitizeTypeName from '../../../utils/sanitizeTypeName';
import { getMappedType } from './getMappedType';
import { stripNamespace } from './stripNamespace';

const encode = (value: string): string => {
return value.replace(/^[^a-zA-Z_$]+/g, '').replace(/[^\w$]+/g, '_');
};
const encode = (value: string): string => sanitizeTypeName(value);

/**
* Parse any string value into a type object.
Expand Down
4 changes: 3 additions & 1 deletion src/openApi/v3/parser/escapeName.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import validTypescriptIdentifierRegex from '../../../utils/validTypescriptIdentifierRegex';

export const escapeName = (value: string): string => {
if (value || value === '') {
const validName = /^[a-zA-Z_$][\w$]+$/g.test(value);
const validName = validTypescriptIdentifierRegex.test(value);
if (!validName) {
return `'${value}'`;
}
Expand Down
7 changes: 2 additions & 5 deletions src/openApi/v3/parser/getEnum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Enum } from '../../../client/interfaces/Enum';
import sanitizeEnumName from '../../../utils/sanitizeEnumName';

export const getEnum = (values?: (string | number)[]): Enum[] => {
if (Array.isArray(values)) {
Expand All @@ -19,11 +20,7 @@ export const getEnum = (values?: (string | number)[]): Enum[] => {
};
}
return {
name: String(value)
.replace(/\W+/g, '_')
.replace(/^(\d+)/g, '_$1')
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
.toUpperCase(),
name: sanitizeEnumName(String(value)),
value: `'${value.replace(/'/g, "\\'")}'`,
type: 'string',
description: null,
Expand Down
9 changes: 3 additions & 6 deletions src/openApi/v3/parser/getOperationName.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import camelCase from 'camelcase';

import sanitizeOperationName from '../../../utils/sanitizeOperationName';

/**
* Convert the input value to a correct operation (method) classname.
* This will use the operation ID - if available - and otherwise fallback
* on a generated name from the URL
*/
export const getOperationName = (url: string, method: string, operationId?: string): string => {
if (operationId) {
return camelCase(
operationId
.replace(/^[^a-zA-Z]+/g, '')
.replace(/[^\w\-]+/g, '-')
.trim()
);
return camelCase(sanitizeOperationName(operationId).trim());
}

const urlWithoutPlaceholders = url
Expand Down
7 changes: 2 additions & 5 deletions src/openApi/v3/parser/getOperationParameterName.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import camelCase from 'camelcase';

import { reservedWords } from '../../../utils/reservedWords';
import sanitizeOperationParameterName from '../../../utils/sanitizeOperationParameterName';

/**
* Replaces any invalid characters from a parameter name.
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
*/
export const getOperationParameterName = (value: string): string => {
const clean = value
.replace(/^[^a-zA-Z]+/g, '')
.replace('[]', 'Array')
.replace(/[^\w\-]+/g, '-')
.trim();
const clean = sanitizeOperationParameterName(value).trim();
return camelCase(clean).replace(reservedWords, '_$1');
};
1 change: 1 addition & 0 deletions src/openApi/v3/parser/getServiceName.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ describe('getServiceName', () => {
expect(getServiceName('@fooBar')).toEqual('FooBar');
expect(getServiceName('$fooBar')).toEqual('FooBar');
expect(getServiceName('123fooBar')).toEqual('FooBar');
expect(getServiceName('non-ascii-æøåÆØÅöôêÊ字符串')).toEqual('NonAsciiÆøåÆøÅöôêÊ字符串');
});
});
7 changes: 3 additions & 4 deletions src/openApi/v3/parser/getServiceName.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import camelCase from 'camelcase';

import sanitizeServiceName from '../../../utils/sanitizeServiceName';

/**
* Convert the input value to a correct service name. This converts
* the input string to PascalCase.
*/
export const getServiceName = (value: string): string => {
const clean = value
.replace(/^[^a-zA-Z]+/g, '')
.replace(/[^\w\-]+/g, '-')
.trim();
const clean = sanitizeServiceName(value).trim();
return camelCase(clean, { pascalCase: true });
};
5 changes: 2 additions & 3 deletions src/openApi/v3/parser/getType.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { Type } from '../../../client/interfaces/Type';
import { isDefined } from '../../../utils/isDefined';
import sanitizeTypeName from '../../../utils/sanitizeTypeName';
import { getMappedType } from './getMappedType';
import { stripNamespace } from './stripNamespace';

const encode = (value: string): string => {
return value.replace(/^[^a-zA-Z_$]+/g, '').replace(/[^\w$]+/g, '_');
};
const encode = (value: string): string => sanitizeTypeName(value);

/**
* Parse any string value into a type object.
Expand Down
11 changes: 11 additions & 0 deletions src/utils/sanitizeEnumName.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import sanitizeEnumName from './sanitizeEnumName';

describe('sanitizeEnumName', () => {
it('should replace illegal characters', () => {
expect(sanitizeEnumName('abc')).toEqual('ABC');
expect(sanitizeEnumName('æbc')).toEqual('ÆBC');
expect(sanitizeEnumName('æb.c')).toEqual('ÆB_C');
expect(sanitizeEnumName('1æb.c')).toEqual('_1ÆB_C');
expect(sanitizeEnumName("'quoted'")).toEqual('_QUOTED_');
});
});
18 changes: 18 additions & 0 deletions src/utils/sanitizeEnumName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Sanitizes names of enums, so they are valid typescript identifiers of a certain form.
*
* 1: Replace all characters not legal as part of identifier with '_'
* 2: Add '_' prefix if first character of enum name has character not legal for start of identifier
* 3: Add '_' where the string transitions from lowercase to uppercase
* 4: Transform the whole string to uppercase
*
* Javascript identifier regexp pattern retrieved from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers
*/
const sanitizeEnumName = (name: string) =>
name
.replace(/[^$\u200c\u200d\p{ID_Continue}]/gu, '_')
.replace(/^([^$_\p{ID_Start}])/u, '_$1')
.replace(/(\p{Lowercase})(\p{Uppercase}+)/gu, '$1_$2')
.toUpperCase();

export default sanitizeEnumName;
7 changes: 7 additions & 0 deletions src/utils/sanitizeOperationName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import sanitizeServiceName from './sanitizeServiceName';

/**
* sanitizeOperationName does the same as sanitizeServiceName.
*/
const sanitizeOperationName = sanitizeServiceName;
export default sanitizeOperationName;
7 changes: 7 additions & 0 deletions src/utils/sanitizeOperationParameterName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import sanitizeOperationName from './sanitizeOperationName';

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

export default sanitizeServiceName;
10 changes: 10 additions & 0 deletions src/utils/sanitizeTypeName.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import sanitizeTypeName from './sanitizeTypeName';

describe('sanitizeTypeName', () => {
it('should remove/replace illegal characters', () => {
expect(sanitizeTypeName('abc')).toEqual('abc');
expect(sanitizeTypeName('æbc')).toEqual('æbc');
expect(sanitizeTypeName('æb.c')).toEqual('æb_c');
expect(sanitizeTypeName('1æb.c')).toEqual('æb_c');
});
});
16 changes: 16 additions & 0 deletions src/utils/sanitizeTypeName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Sanitizes names of types, so they are valid typescript identifiers of a certain form.
*
* 1: Remove any leading characters that are illegal as starting character of a typescript identifier.
* 2: Replace illegal characters in remaining part of type name with underscore (_).
*
* Step 1 should perhaps instead also replace illegal characters with underscore, or prefix with it, like sanitizeEnumName
* does. The way this is now one could perhaps end up removing all characters, if all are illegal start characters. It
* would be sort of a breaking change to do so, though, previously generated code might change then.
*
* Javascript identifier regexp pattern retrieved from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers
*/
const sanitizeTypeName = (name: string) =>
name.replace(/^[^$_\p{ID_Start}]+/u, '').replace(/[^$\u200c\u200d\p{ID_Continue}]/gu, '_');

export default sanitizeTypeName;
4 changes: 4 additions & 0 deletions src/utils/validTypescriptIdentifierRegex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Javascript identifier regexp pattern retrieved from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers
const validTypescriptIdentifierRegex = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u;

export default validTypescriptIdentifierRegex;
Loading