-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(compiler): add initial HObject transformer
- Loading branch information
Showing
8 changed files
with
723 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
src/Compiler/Transformer/Feature/HObject/HObjectConstructorTsFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import ts from "typescript"; | ||
import type { HObjectPropertyTsMeta } from "./HObjectPropertyTsMeta"; | ||
|
||
export class HObjectToConstructorTsFactory { | ||
public create(properties: HObjectPropertyTsMeta[]): ts.ConstructorDeclaration { | ||
const propertiesSorted = [...properties.filter(p => !p.optional), ...properties.filter(p => p.optional)]; | ||
const constructorParameters = propertiesSorted.map(p => this.createParameter(p)); | ||
const constructorAssignments = propertiesSorted.map(p => this.createAssignment(p)); | ||
|
||
return ts.factory.createConstructorDeclaration( | ||
[ts.factory.createModifier(ts.SyntaxKind.PublicKeyword)], | ||
constructorParameters, | ||
ts.factory.createBlock(constructorAssignments, true) | ||
); | ||
} | ||
|
||
private createParameter(p: HObjectPropertyTsMeta): ts.ParameterDeclaration { | ||
return ts.factory.createParameterDeclaration( | ||
undefined, | ||
undefined, | ||
ts.factory.createIdentifier(p.name), | ||
p.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, | ||
ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), | ||
undefined | ||
); | ||
} | ||
|
||
private createAssignment(p: HObjectPropertyTsMeta): ts.Statement { | ||
return ts.factory.createExpressionStatement( | ||
ts.factory.createBinaryExpression( | ||
ts.factory.createPropertyAccessExpression(ts.factory.createThis(), p.name), | ||
ts.factory.createToken(ts.SyntaxKind.EqualsToken), | ||
ts.factory.createIdentifier(p.name) | ||
) | ||
); | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
src/Compiler/Transformer/Feature/HObject/HObjectParseTsFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
|
||
import ts from "typescript"; | ||
import type { HObjectPropertyTsMeta } from "./HObjectPropertyTsMeta"; | ||
|
||
import type { ImportDeclarationWrapper } from "../../Helper/ImportDeclarationWrapper"; | ||
import { TsTransfromerHelper } from "../../TsTransformerHelper"; | ||
import { HObjectPropertyParseTsFactory } from "./HObjectPropertyParseTsFactory"; | ||
|
||
export class HObjectParseTsFactory { | ||
|
||
private propertyTsFactory: HObjectPropertyParseTsFactory; | ||
|
||
public constructor() { | ||
this.propertyTsFactory = new HObjectPropertyParseTsFactory(); | ||
} | ||
|
||
public create(hObjectClassName: string, properties: HObjectPropertyTsMeta[], hCommonImportDecl: ImportDeclarationWrapper): ts.MethodDeclaration { | ||
const returnType = ts.factory.createTypeReferenceNode( | ||
hCommonImportDecl.getEntityName('R'), [ts.factory.createTypeReferenceNode(hObjectClassName, [])] | ||
); | ||
|
||
const parameters = [ | ||
ts.factory.createParameterDeclaration(undefined, undefined, 'plain', undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)) | ||
]; | ||
|
||
return ts.factory.createMethodDeclaration( | ||
[ts.factory.createModifier(ts.SyntaxKind.PublicKeyword), ts.factory.createModifier(ts.SyntaxKind.StaticKeyword)], | ||
undefined, // * | ||
'parse', | ||
undefined, // '?' | ||
undefined, // generic | ||
parameters, | ||
returnType, | ||
ts.factory.createBlock(this.createBody(hObjectClassName, properties), true) | ||
); | ||
} | ||
|
||
private createBody(hObjectClassName: string, properties: HObjectPropertyTsMeta[]): ts.Statement[] { | ||
|
||
const statements: ts.Statement[] = [ | ||
this.createCheckIsObject(hObjectClassName), | ||
this.createPlainObjVarDeclaration(hObjectClassName), | ||
this.createIssuesVarDeclaration(), | ||
]; | ||
|
||
for (const p of properties) { | ||
statements.push(...this.propertyTsFactory.create(p)); | ||
} | ||
|
||
return statements; | ||
} | ||
|
||
/** | ||
` | ||
if (typeof plain !== 'object') { | ||
return PlainParseHelper.HObjectIsNotObjectParseErr(hObjectClassName as any, plain]); | ||
} | ||
` | ||
*/ | ||
private createCheckIsObject(hObjectClassName: string): ts.Statement { | ||
// `typeof plain !== 'object'` | ||
const condtionExpression = ts.factory.createBinaryExpression( | ||
ts.factory.createTypeOfExpression(ts.factory.createIdentifier('plain')), | ||
ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), | ||
ts.factory.createStringLiteral('object') | ||
); | ||
|
||
const block = ts.factory.createBlock([ | ||
ts.factory.createReturnStatement( | ||
ts.factory.createCallExpression( | ||
ts.factory.createPropertyAccessExpression( | ||
ts.factory.createIdentifier('PlainParseHelper'), | ||
'HObjectIsNotObjectParseErr' | ||
), | ||
undefined, | ||
[ | ||
ts.factory.createAsExpression( | ||
ts.factory.createIdentifier(hObjectClassName), | ||
ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) | ||
), | ||
ts.factory.createIdentifier('plain') | ||
] | ||
) | ||
) | ||
], true); | ||
|
||
return ts.factory.createIfStatement(condtionExpression, block); | ||
} | ||
|
||
// `const p = plain as Record<keyof TestDto, unknown>;` | ||
private createPlainObjVarDeclaration(hObjectClassName: string): ts.Statement { | ||
const initializer = ts.factory.createAsExpression( | ||
ts.factory.createIdentifier('plain'), | ||
ts.factory.createTypeReferenceNode( | ||
ts.factory.createIdentifier('Record'), | ||
[ | ||
ts.factory.createTypeOperatorNode(ts.SyntaxKind.KeyOfKeyword, ts.factory.createTypeReferenceNode(hObjectClassName, undefined)), | ||
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword), | ||
] | ||
) | ||
); | ||
return TsTransfromerHelper.createConstStatement("p", undefined, initializer); | ||
} | ||
|
||
// `const issues: PlainParseIssue[] = [];` | ||
private createIssuesVarDeclaration() { | ||
const type = ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode('PlainParseIssue', undefined)); | ||
const initializer = ts.factory.createArrayLiteralExpression([], false); | ||
return TsTransfromerHelper.createConstStatement("issues", type, initializer); | ||
|
||
} | ||
} |
122 changes: 122 additions & 0 deletions
122
src/Compiler/Transformer/Feature/HObject/HObjectPropertyParseTsFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import ts from "typescript"; | ||
import { CollectionType, HObjectPropertyPrimitiveType, HObjectPropertyTsMeta } from "./HObjectPropertyTsMeta"; | ||
import { TsTransfromerHelper } from "../../TsTransformerHelper"; | ||
|
||
export class HObjectPropertyParseTsFactory { | ||
public create(meta: HObjectPropertyTsMeta): ts.Statement[] { | ||
if (meta.isPrimitive()) { | ||
switch (meta.tsType) { | ||
case HObjectPropertyPrimitiveType.string: return this.createStringParse(meta); | ||
} | ||
} else { | ||
// TODO HObject parse create | ||
return []; | ||
} | ||
|
||
return []; | ||
//throw new LogicError('Unspported property meta in parse(), meta: ' + meta); | ||
|
||
} | ||
|
||
private createStringParse(meta: HObjectPropertyTsMeta): ts.Statement[] { | ||
/* | ||
let initializer; | ||
if (meta.validationRules) { | ||
if (meta.validationRules[0].isStringLengthRule()) { | ||
switch (meta.validationRules[0].extraRuleParts.join('.')) { | ||
case "length": | ||
initializer = this.createPrimitivePropertyParseCode(meta, "parseStringLength", { | ||
scalar: meta.validationRules[0].args[0], | ||
}); | ||
break; | ||
case "length.min": | ||
initializer = | ||
break; | ||
case "length.max": | ||
initializer = | ||
break; | ||
} | ||
} | ||
} else { | ||
initializer = this.createPrimitivePropertyParseCode(meta, "parseString"); | ||
}* */ | ||
|
||
const initializer = this.createPrimitivePropertyParseCode(meta, "parseString"); | ||
return meta.optional | ||
? this.createOptionalPropertyParse(meta, initializer) | ||
: [TsTransfromerHelper.createConstStatement(meta.name, undefined, initializer)]; | ||
} | ||
|
||
private createPrimitivePropertyParseCode(meta: HObjectPropertyTsMeta, helperMethod: string): ts.Expression { | ||
if (meta.collectionType === CollectionType.Array) { | ||
return this.createParsePrimitiveArray(meta, helperMethod); | ||
} else { | ||
return this.createPlainParseHelperCall(meta, helperMethod); | ||
} | ||
} | ||
|
||
private createOptionalPropertyParse(meta: HObjectPropertyTsMeta, initializer: ts.Expression): ts.Statement[] { | ||
const letPropertyVar = TsTransfromerHelper.createLetStatement(meta.name); | ||
// `p.propName === undefined` | ||
const ifCondition = ts.factory.createBinaryExpression( | ||
this.createPlainObjPropertyAccess(meta), | ||
ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), | ||
ts.factory.createIdentifier('undefined') | ||
); | ||
|
||
const optionalPropertyIf = ts.factory.createIfStatement( | ||
ifCondition, | ||
ts.factory.createBlock([this.createAssignInitializerToLetVar(meta, initializer)], true) | ||
); | ||
return [letPropertyVar, optionalPropertyIf]; | ||
} | ||
|
||
private createAssignInitializerToLetVar(meta: HObjectPropertyTsMeta, initializer: ts.Expression): ts.Statement { | ||
return ts.factory.createExpressionStatement( | ||
ts.factory.createBinaryExpression( | ||
ts.factory.createIdentifier(meta.name), | ||
ts.factory.createToken(ts.SyntaxKind.EqualsToken), | ||
initializer, | ||
) | ||
); | ||
} | ||
|
||
private createParsePrimitiveArray(meta: HObjectPropertyTsMeta, parse: ts.Expression | string) { | ||
parse = typeof parse === 'string' ? this.createPlainParseHelperPropertyAccess(parse) : parse; | ||
return this.createPlainParseHelperCall(meta, "parsePrimitiveArray", [parse]); | ||
} | ||
|
||
private createPlainParseHelperCall(meta: HObjectPropertyTsMeta, method: string, args: ts.Expression[] = []) { | ||
return ts.factory.createCallExpression( | ||
this.createPlainParseHelperPropertyAccess(method), | ||
undefined, | ||
[ | ||
this.createPlainObjPropertyAccess(meta), | ||
...args, | ||
ts.factory.createStringLiteral(meta.name), | ||
ts.factory.createIdentifier('issues') | ||
] | ||
); | ||
} | ||
|
||
private createPlainParseHelperPropertyAccess(name: string): ts.PropertyAccessExpression { | ||
return ts.factory.createPropertyAccessExpression( | ||
ts.factory.createIdentifier("PlainParseHelper"), | ||
ts.factory.createIdentifier(name) | ||
); | ||
} | ||
|
||
private createPlainObjPropertyAccess(meta: HObjectPropertyTsMeta): ts.PropertyAccessExpression { | ||
return ts.factory.createPropertyAccessExpression( | ||
this.createPlainObjIdentifier(), | ||
ts.factory.createIdentifier(meta.name) | ||
); | ||
} | ||
|
||
private createPlainObjIdentifier(): ts.Identifier { | ||
return ts.factory.createIdentifier("p"); | ||
} | ||
|
||
|
||
|
||
} |
96 changes: 96 additions & 0 deletions
96
src/Compiler/Transformer/Feature/HObject/HObjectToJSONTsFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import ts from "typescript"; | ||
import { CollectionType, HObjectPropertyTsMeta, HObjectPropertyPrimitiveType, type PrimitiveHObjectPropertyTsMeta } from "./HObjectPropertyTsMeta"; | ||
import type { ImportDeclarationWrapper } from "../../Helper/ImportDeclarationWrapper"; | ||
|
||
export class HObjectToJSONTsFactory { | ||
|
||
public create(hObjectClassName: string, properties: HObjectPropertyTsMeta[], hCommonImportDecl: ImportDeclarationWrapper): ts.MethodDeclaration { | ||
const returnType = ts.factory.createTypeReferenceNode( | ||
hCommonImportDecl.getEntityName('JsonObjectType'), [ts.factory.createTypeReferenceNode(hObjectClassName, [])] | ||
); | ||
|
||
return ts.factory.createMethodDeclaration( | ||
[ts.factory.createModifier(ts.SyntaxKind.PublicKeyword), ts.factory.createModifier(ts.SyntaxKind.StaticKeyword)], | ||
undefined, // * | ||
'toJSON', | ||
undefined, // '?' | ||
undefined, // generic | ||
[], | ||
returnType, | ||
ts.factory.createBlock(this.createBody(properties), true) | ||
); | ||
} | ||
|
||
private createBody(properties: HObjectPropertyTsMeta[]): ts.Statement[] { | ||
const props: ts.ObjectLiteralElementLike[] = []; | ||
for (const p of properties) { | ||
const initializer = p.isPrimitive() ? this.createPrimitivePropInitializer(p) : this.createHObjectPropInitializer(p); | ||
props.push(ts.factory.createPropertyAssignment(p.name, initializer)); | ||
} | ||
|
||
return [ | ||
ts.factory.createReturnStatement(ts.factory.createObjectLiteralExpression(props, true)) | ||
]; | ||
} | ||
|
||
private createPrimitivePropInitializer(p: PrimitiveHObjectPropertyTsMeta): ts.Expression { | ||
if (p.tsType === HObjectPropertyPrimitiveType.bigint) { | ||
if (p.collectionType === CollectionType.Array) { | ||
return this.createMethodOfPropCall( | ||
ts.factory.createThis(), p.name, 'map', | ||
this.createArrayMapCallArgs(this.createMethodCall('item', 'toString', []),), | ||
p.optional | ||
); | ||
} | ||
return this.createMethodOfPropCall(ts.factory.createThis(), p.name, 'toString', [], p.optional); | ||
} | ||
|
||
return ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier(p.name)); | ||
} | ||
|
||
private createHObjectPropInitializer(p: HObjectPropertyTsMeta): ts.Expression { | ||
if (p.collectionType === CollectionType.Array) { | ||
return this.createMethodOfPropCall( | ||
ts.factory.createThis(), p.name, 'map', | ||
this.createArrayMapCallArgs(this.createMethodCall('item', 'toJSON', []),), | ||
p.optional | ||
); | ||
} | ||
|
||
return ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier(p.name)); | ||
} | ||
|
||
private createArrayMapCallArgs(body: ts.ConciseBody, itemVarName = 'item') { | ||
return [ | ||
ts.factory.createArrowFunction( | ||
undefined, | ||
undefined, | ||
[ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier(itemVarName))], | ||
undefined, | ||
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), | ||
body | ||
) | ||
]; | ||
} | ||
|
||
private createMethodOfPropCall(objExp: ts.Expression, prop: string, method: string, args: ts.Expression[], optional: boolean): ts.CallExpression { | ||
return ts.factory.createCallChain( | ||
ts.factory.createPropertyAccessExpression( | ||
ts.factory.createPropertyAccessExpression(objExp, ts.factory.createIdentifier(prop)), | ||
ts.factory.createIdentifier(method) | ||
), | ||
optional ? ts.factory.createToken(ts.SyntaxKind.QuestionDotToken) : undefined, | ||
undefined, | ||
args | ||
); | ||
} | ||
|
||
private createMethodCall(varName: string, method: string, args: ts.Expression[]): ts.CallExpression { | ||
return ts.factory.createCallExpression( | ||
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(varName), method), | ||
undefined, | ||
args, | ||
); | ||
} | ||
|
||
} |
Oops, something went wrong.