Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix #46: Handle most of quoted property name issues
Browse files Browse the repository at this point in the history
Embraser01 committed Oct 25, 2023
1 parent 5202654 commit 30c2efe
Showing 17 changed files with 198 additions and 23 deletions.
41 changes: 36 additions & 5 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions packages/typoas-generator/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testPathIgnorePatterns: ['/node_modules/', '/lib/'],
};
7 changes: 6 additions & 1 deletion packages/typoas-generator/package.json
Original file line number Diff line number Diff line change
@@ -11,7 +11,8 @@
"scripts": {
"prepack": "tsc",
"test:jest": "jest",
"test:types": "tsc --noEmit"
"test:types": "tsc --noEmit",
"generate-identifier-regexp": "ts-node ./scripts/generate-identifier-regexp.ts"
},
"dependencies": {
"lodash": "^4.17.21",
@@ -22,9 +23,13 @@
"@jest/globals": "^29.5.0",
"@types/lodash": "^4.14.194",
"@types/node": "^20.2.3",
"@types/regenerate": "^1.4.2",
"@unicode/unicode-9.0.0": "^1.5.2",
"jest": "^29.5.0",
"jest-environment-node": "^29.5.0",
"regenerate": "^1.4.2",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
},
"files": [
51 changes: 51 additions & 0 deletions packages/typoas-generator/scripts/generate-identifier-regexp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Based on https://gist.github.com/mathiasbynens/6334847 by @mathias
*/
import { writeFile } from 'node:fs/promises';
import regenerate from 'regenerate';

// Which Unicode version should be used
const version = '9.0.0';

// Set up a shorthand function to import Unicode data.
async function get(what: string): Promise<number[]> {
const { default: codePoints } = await import(
`@unicode/unicode-${version}/${what}/code-points.js`
);
return codePoints;
}

async function main() {
// Get the Unicode properties needed to construct the ES6 regex.
const ID_Start = await get('Binary_Property/ID_Start');
const ID_Continue = await get('Binary_Property/ID_Continue');
const Other_ID_Start = await get('Binary_Property/Other_ID_Start');

// http://ecma-international.org/ecma-262/6.0/#sec-identifier-names-static-semantics-early-errors
// http://unicode.org/reports/tr31/#Default_Identifier_Syntax
// https://bugs.ecmascript.org/show_bug.cgi?id=2717#c0
const identifierStart = regenerate(ID_Start)
// Note: this already includes `Other_ID_Start`. http://git.io/wRCAfQ
.add('$', '_');
const identifierPart = regenerate(ID_Continue)
// Note: `ID_Continue` already includes `Other_ID_Continue`. http://git.io/wRCAfQ
.add(Other_ID_Start)
.add('$', '_', '\u200C', '\u200D');

const fileContent = `/* eslint-disable no-misleading-character-class,no-useless-escape */
// This file is generated by scripts/generate-identifier-regexp.ts
// Do not edit it manually.
export const identifierStartRegexp =
/${identifierStart.toString()}/;
export const identifierPartRegexp =
/${identifierPart.toString()}/;
export const es6IdentifierRegexp =
/^(?:${identifierStart.toString()})(?:${identifierPart.toString()})*$/;
`;

await writeFile('./src/generator/utils/identifier-regexps.ts', fileContent);
}

main();
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ import {
getParameterName,
isParameterRequired,
} from '../../components/parameters';
import { hasUnsupportedIdentifierChar } from '../../utils/operation-name';
import { isInvalidES6IdentifierName } from '../../utils/operation-name';
import { createSchemaTypeFromRequestBody } from '../../components/request-bodies';
import { GlobalParameters } from './types';
import { createSchemaTypeFromResponse } from '../../components/responses';
@@ -61,7 +61,7 @@ export function createOperationDeclaration(

return factory.createPropertySignature(
undefined,
hasUnsupportedIdentifierChar(name)
isInvalidES6IdentifierName(name)
? factory.createStringLiteral(name, true)
: factory.createIdentifier(name),
isParameterRequired(p, ctx)
10 changes: 5 additions & 5 deletions packages/typoas-generator/src/generator/api/security.ts
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import {
createConfigTypeFromSecurityScheme,
createRuntimeSecurityClass,
} from '../components/security-scheme';
import { hasUnsupportedIdentifierChar } from '../utils/operation-name';
import { isInvalidES6IdentifierName } from '../utils/operation-name';

export const AUTH_TYPE_NAME = 'AuthMethods';

@@ -27,7 +27,7 @@ export function createAuthMethodsType(
Object.entries(securitySchemes).map(([name, sec]) =>
factory.createPropertySignature(
undefined,
hasUnsupportedIdentifierChar(name)
isInvalidES6IdentifierName(name)
? factory.createStringLiteral(name, true)
: factory.createIdentifier(name),
factory.createToken(SyntaxKind.QuestionToken),
@@ -70,11 +70,11 @@ export function createConfigureAuthFunction(
factory.createObjectLiteralExpression(
Object.entries(securitySchemes).map(([name, sec]) =>
factory.createPropertyAssignment(
hasUnsupportedIdentifierChar(name)
isInvalidES6IdentifierName(name)
? factory.createStringLiteral(name, true)
: factory.createIdentifier(name),
factory.createLogicalAnd(
hasUnsupportedIdentifierChar(name)
isInvalidES6IdentifierName(name)
? factory.createElementAccessChain(
factory.createIdentifier('params'),
factory.createToken(SyntaxKind.QuestionDotToken),
@@ -87,7 +87,7 @@ export function createConfigureAuthFunction(
),
createRuntimeSecurityClass(
sec,
hasUnsupportedIdentifierChar(name)
isInvalidES6IdentifierName(name)
? factory.createElementAccessExpression(
factory.createIdentifier('params'),
factory.createStringLiteral(name),
Original file line number Diff line number Diff line change
@@ -14,6 +14,18 @@ exports[`create type from schema should handle object schema without properties
}"
`;

exports[`create type from schema should handle objects with special named properties 1`] = `
"{
'1'?: number;
'^Test^'?: string;
await?: string;
b?: number;
ʱ?: number;
_1?: number;
'page[number]'?: number;
}"
`;

exports[`create type from schema should handle primitive boolean enums 1`] = `"true"`;

exports[`create type from schema should handle primitive boolean schema 1`] = `"boolean"`;
Original file line number Diff line number Diff line change
@@ -120,6 +120,25 @@ describe('create type from schema', () => {
expect(getStringFromNode(node)).toMatchSnapshot();
});

it('should handle objects with special named properties', () => {
const schema: SchemaObject = {
type: 'object',
required: ['a'],
properties: {
'^Test^': { type: 'string' },
await: { type: 'string' },
b: { type: 'number' },
ʱ: { type: 'number' },
_1: { type: 'number' },
1: { type: 'number' },
'page[number]': { type: 'number' },
},
};

const node = createTypeFromSchema(schema, new Context());
expect(getStringFromNode(node)).toMatchSnapshot();
});

describe('with complex object', () => {
it('should create nested objects', () => {
const schema: SchemaObject = {
Loading

0 comments on commit 30c2efe

Please sign in to comment.