Skip to content

Commit a4b0701

Browse files
authored
use classes option to generate classes instead of interfaces (#21)
* use classes option to generate classes instead of interfaces * sort classes * 2.14.3
1 parent eff55db commit a4b0701

File tree

5 files changed

+99
-25
lines changed

5 files changed

+99
-25
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@themost/client",
3-
"version": "2.14.2",
3+
"version": "2.14.3",
44
"description": "MOST Web Framework Codename Blueshift - Client Common",
55
"module": "dist/index.esm.js",
66
"main": "dist/index.js",

util/bin/cli.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ async function main() {
1313
console.log('Usage: client-cli <source> [options]');
1414
console.log('Options:');
1515
console.log(' --out-file <output> The output file to write the rendered types to');
16+
console.log(' --classes Render classes instead of interfaces');
1617
return;
1718
}
1819
const source = args._[0];
@@ -21,7 +22,10 @@ async function main() {
2122
return process.exit(-1);
2223
}
2324
const isURL = source.startsWith('http://') || source.startsWith('https://');
24-
const typeRenderer = isURL ? new TypeRenderer(source) : new FileSchemaRenderer(source);
25+
const options = {
26+
classes: args.classes
27+
};
28+
const typeRenderer = isURL ? new TypeRenderer(source, options) : new FileSchemaRenderer(source, options);
2529
const result = await typeRenderer.renderAny();
2630
if (args.outFile) {
2731
writeFileSync(args.outFile, result);

util/spec/TypeRenderer.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ describe("TypeRenderer", () => {
2222
expect(typeDeclarations).toBeInstanceOf(String);
2323
});
2424

25-
25+
it("should render any type as class", async () => {
26+
const renderer = new TypeRenderer('http://localhost:8080/api/', {
27+
classes: true
28+
});
29+
const typeDeclarations = await renderer.renderAny();
30+
expect(typeDeclarations).toBeInstanceOf(String);
31+
});
2632

2733
});

util/src/TypeRenderer.ts

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,20 @@ const EdmTypeMap = new Map([
4141
]
4242
]);
4343

44+
const Space = ' ';
45+
const OpeningBracket = '{';
46+
const ClosingBracket = '}';
47+
const NewLine = '\n';
48+
const Tab = '\t';
49+
4450
class TypeRenderer {
4551

4652
protected context: BasicDataContext;
4753
protected schema: EdmSchema;
4854

49-
/**
50-
* @param {string} host
51-
*/
52-
constructor(host?: string) {
55+
constructor(host?: string, protected options?: {
56+
classes: boolean
57+
}) {
5358
this.context = new BasicDataContext(host);
5459
}
5560

@@ -87,7 +92,6 @@ class TypeRenderer {
8792
* @returns string
8893
*/
8994
protected renderType(entityType: EdmEntityType) {
90-
const extendsInterface = entityType.BaseType ? ` extends ${entityType.BaseType} ` : '';
9195
const properties = entityType.Property.map((property) => {
9296
const { Name } = property;
9397
const Declaration = this.renderProperty(property);
@@ -104,15 +108,32 @@ class TypeRenderer {
104108
Declaration
105109
}
106110
}));
107-
const result = `
108-
export interface ${entityType.Name} ${extendsInterface}{
109-
${properties.sort(
110-
(a, b) => {
111-
if (a.Name < b.Name) return -1;
112-
if (a.Name > b.Name) return 1;
113-
return 0;
114-
}).map((property) => `\t${property.Declaration}`).join('\n')}
115-
}`
111+
let result = '';
112+
if (this.options && this.options.classes) {
113+
// find entity set and define annotation
114+
const entitySet = this.schema.EntityContainer.EntitySet.find((s) => s.EntityType === entityType.Name);
115+
if (entitySet) {
116+
result += `@EdmSchema.entitySet('${entitySet.Name}')`;
117+
result += NewLine;
118+
}
119+
}
120+
result += 'export';
121+
result += Space;
122+
result += this.options && this.options.classes ? 'class' : 'interface';
123+
result += Space;
124+
result += entityType.Name;
125+
result += Space;
126+
result += entityType.BaseType ? `extends ${entityType.BaseType}` : '';
127+
result += Space;
128+
result += OpeningBracket;
129+
result += NewLine;
130+
result += properties.sort((a, b) => {
131+
if (a.Name < b.Name) return -1;
132+
if (a.Name > b.Name) return 1;
133+
return 0;
134+
}).map((property) => Tab + `${property.Declaration}`).join(NewLine);
135+
result += NewLine;
136+
result += ClosingBracket;
116137
return result.replace(/(\n+)/g, '\n');
117138
}
118139

@@ -132,20 +153,63 @@ ${properties.sort(
132153
if (this.schema == null) {
133154
this.schema = await this.getSchema();
134155
}
135-
const typeDeclarations = this.schema.EntityType.sort(
136-
(a, b) => {
137-
if (a.Name < b.Name) return -1;
138-
if (a.Name > b.Name) return 1;
139-
return 0;
156+
157+
const sort = (a: string, b: string) => {
158+
if (a < b) return -1;
159+
if (a > b) return 1;
160+
return 0;
161+
}
162+
163+
// sort entity types
164+
const withoutBaseType = this.schema.EntityType
165+
.filter((t) => t.BaseType == null)
166+
.map((t) => t.Name)
167+
.sort((a, b) => sort(a, b));
168+
169+
const names = [];
170+
names.push(...withoutBaseType);
171+
const withBaseType = this.schema.EntityType
172+
.filter((t) => t.BaseType != null)
173+
.map((t) => t.Name)
174+
.sort((a, b) => sort(a, b));
175+
// sort entity types with base type and find base type path e.g. Action/RequestAction/StudentRequestAction
176+
const withTypes = withBaseType.map((t) => {
177+
let entityType = this.schema.EntityType.find((e) => e.Name === t)
178+
let baseType = entityType.BaseType;
179+
let types = [
180+
t
181+
];
182+
while (baseType) {
183+
types.push(baseType);
184+
entityType = this.schema.EntityType.find((e) => e.Name === baseType);
185+
baseType = entityType ? entityType.BaseType : null;
186+
}
187+
return {
188+
Name: t,
189+
Types: types.reverse().join('/')
140190
}
191+
}).sort((a, b) => sort(a.Types, b.Types)).map((t) => t.Name);
192+
names.push(...withTypes);
193+
const typeDeclarations = names.map(
194+
(name: string) => this.schema.EntityType.find((t) => t.Name === name)
141195
).map((entityType) => this.renderType(entityType));
142-
return typeDeclarations.join('\n');
196+
let result = '';
197+
if (this.options && this.options.classes) {
198+
result += 'import { EdmSchema } from \'@themost/client\';';
199+
result += NewLine;
200+
result += NewLine;
201+
}
202+
result += typeDeclarations.join(NewLine + NewLine);
203+
return result;
143204
}
144205
}
145206

146207
class FileSchemaRenderer extends TypeRenderer {
147-
constructor(private file: string) {
208+
constructor(private file: string, options?: {
209+
classes: boolean
210+
}) {
148211
super();
212+
this.options = options;
149213
}
150214

151215
protected getSchema(): Promise<EdmSchema> {

0 commit comments

Comments
 (0)