Skip to content

Commit

Permalink
use classes option to generate classes instead of interfaces (#21)
Browse files Browse the repository at this point in the history
* use classes option to generate classes instead of interfaces

* sort classes

* 2.14.3
  • Loading branch information
kbarbounakis authored Apr 2, 2024
1 parent eff55db commit a4b0701
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 25 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@themost/client",
"version": "2.14.2",
"version": "2.14.3",
"description": "MOST Web Framework Codename Blueshift - Client Common",
"module": "dist/index.esm.js",
"main": "dist/index.js",
Expand Down
6 changes: 5 additions & 1 deletion util/bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ async function main() {
console.log('Usage: client-cli <source> [options]');
console.log('Options:');
console.log(' --out-file <output> The output file to write the rendered types to');
console.log(' --classes Render classes instead of interfaces');
return;
}
const source = args._[0];
Expand All @@ -21,7 +22,10 @@ async function main() {
return process.exit(-1);
}
const isURL = source.startsWith('http://') || source.startsWith('https://');
const typeRenderer = isURL ? new TypeRenderer(source) : new FileSchemaRenderer(source);
const options = {
classes: args.classes
};
const typeRenderer = isURL ? new TypeRenderer(source, options) : new FileSchemaRenderer(source, options);
const result = await typeRenderer.renderAny();
if (args.outFile) {
writeFileSync(args.outFile, result);
Expand Down
8 changes: 7 additions & 1 deletion util/spec/TypeRenderer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ describe("TypeRenderer", () => {
expect(typeDeclarations).toBeInstanceOf(String);
});


it("should render any type as class", async () => {
const renderer = new TypeRenderer('http://localhost:8080/api/', {
classes: true
});
const typeDeclarations = await renderer.renderAny();
expect(typeDeclarations).toBeInstanceOf(String);
});

});
106 changes: 85 additions & 21 deletions util/src/TypeRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ const EdmTypeMap = new Map([
]
]);

const Space = ' ';
const OpeningBracket = '{';
const ClosingBracket = '}';
const NewLine = '\n';
const Tab = '\t';

class TypeRenderer {

protected context: BasicDataContext;
protected schema: EdmSchema;

/**
* @param {string} host
*/
constructor(host?: string) {
constructor(host?: string, protected options?: {
classes: boolean
}) {
this.context = new BasicDataContext(host);
}

Expand Down Expand Up @@ -87,7 +92,6 @@ class TypeRenderer {
* @returns string
*/
protected renderType(entityType: EdmEntityType) {
const extendsInterface = entityType.BaseType ? ` extends ${entityType.BaseType} ` : '';
const properties = entityType.Property.map((property) => {
const { Name } = property;
const Declaration = this.renderProperty(property);
Expand All @@ -104,15 +108,32 @@ class TypeRenderer {
Declaration
}
}));
const result = `
export interface ${entityType.Name} ${extendsInterface}{
${properties.sort(
(a, b) => {
if (a.Name < b.Name) return -1;
if (a.Name > b.Name) return 1;
return 0;
}).map((property) => `\t${property.Declaration}`).join('\n')}
}`
let result = '';
if (this.options && this.options.classes) {
// find entity set and define annotation
const entitySet = this.schema.EntityContainer.EntitySet.find((s) => s.EntityType === entityType.Name);
if (entitySet) {
result += `@EdmSchema.entitySet('${entitySet.Name}')`;
result += NewLine;
}
}
result += 'export';
result += Space;
result += this.options && this.options.classes ? 'class' : 'interface';
result += Space;
result += entityType.Name;
result += Space;
result += entityType.BaseType ? `extends ${entityType.BaseType}` : '';
result += Space;
result += OpeningBracket;
result += NewLine;
result += properties.sort((a, b) => {
if (a.Name < b.Name) return -1;
if (a.Name > b.Name) return 1;
return 0;
}).map((property) => Tab + `${property.Declaration}`).join(NewLine);
result += NewLine;
result += ClosingBracket;
return result.replace(/(\n+)/g, '\n');
}

Expand All @@ -132,20 +153,63 @@ ${properties.sort(
if (this.schema == null) {
this.schema = await this.getSchema();
}
const typeDeclarations = this.schema.EntityType.sort(
(a, b) => {
if (a.Name < b.Name) return -1;
if (a.Name > b.Name) return 1;
return 0;

const sort = (a: string, b: string) => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}

// sort entity types
const withoutBaseType = this.schema.EntityType
.filter((t) => t.BaseType == null)
.map((t) => t.Name)
.sort((a, b) => sort(a, b));

const names = [];
names.push(...withoutBaseType);
const withBaseType = this.schema.EntityType
.filter((t) => t.BaseType != null)
.map((t) => t.Name)
.sort((a, b) => sort(a, b));
// sort entity types with base type and find base type path e.g. Action/RequestAction/StudentRequestAction
const withTypes = withBaseType.map((t) => {
let entityType = this.schema.EntityType.find((e) => e.Name === t)
let baseType = entityType.BaseType;
let types = [
t
];
while (baseType) {
types.push(baseType);
entityType = this.schema.EntityType.find((e) => e.Name === baseType);
baseType = entityType ? entityType.BaseType : null;
}
return {
Name: t,
Types: types.reverse().join('/')
}
}).sort((a, b) => sort(a.Types, b.Types)).map((t) => t.Name);
names.push(...withTypes);
const typeDeclarations = names.map(
(name: string) => this.schema.EntityType.find((t) => t.Name === name)
).map((entityType) => this.renderType(entityType));
return typeDeclarations.join('\n');
let result = '';
if (this.options && this.options.classes) {
result += 'import { EdmSchema } from \'@themost/client\';';
result += NewLine;
result += NewLine;
}
result += typeDeclarations.join(NewLine + NewLine);
return result;
}
}

class FileSchemaRenderer extends TypeRenderer {
constructor(private file: string) {
constructor(private file: string, options?: {
classes: boolean
}) {
super();
this.options = options;
}

protected getSchema(): Promise<EdmSchema> {
Expand Down

0 comments on commit a4b0701

Please sign in to comment.