Skip to content

Commit e57f0c5

Browse files
create directories
1 parent cb47c8b commit e57f0c5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1143
-1
lines changed

src/generators.js

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import TypescriptInterfaceGenerator from "./generators/TypescriptInterfaceGenera
77
import VueGenerator from "./generators/VueGenerator.js";
88
import VuetifyGenerator from "./generators/VuetifyGenerator.js";
99
import QuasarGenerator from "./generators/QuasarGenerator.js";
10+
import AngularGenerator from "./generators/AngularGenerator.js";
1011

1112
function wrap(cl) {
1213
return ({ hydraPrefix, templateDirectory }) =>
@@ -36,5 +37,7 @@ export default async function generators(generator = "react") {
3637
return wrap(VuetifyGenerator);
3738
case "quasar":
3839
return wrap(QuasarGenerator);
40+
case "angular":
41+
return wrap(AngularGenerator);
3942
}
4043
}

src/generators/AngularGenerator.js

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import BaseGenerator from "./BaseGenerator.js";
2+
import handlebars from "handlebars";
3+
import hbhComparison from "handlebars-helpers/lib/comparison.js";
4+
import hbhString from "handlebars-helpers/lib/string.js";
5+
6+
export default class extends BaseGenerator {
7+
constructor(params) {
8+
super(params);
9+
10+
this.registerTemplates("angular/", [
11+
// COMMON COMPONENTS
12+
"app/components/common/delete/delete.component.html",
13+
"app/components/common/delete/delete.component.ts",
14+
"app/components/common/form/form.component.html",
15+
"app/components/common/form/form.component.ts",
16+
"app/components/common/header/header.component.html",
17+
"app/components/common/header/header.component.ts",
18+
"app/components/common/sidebar/sidebar.component.html",
19+
"app/components/common/sidebar/sidebar.component.ts",
20+
"app/components/common/table/table.component.html",
21+
"app/components/common/table/table.component.ts",
22+
23+
// COMPONENTS
24+
"app/components/foo/create/create.component.html",
25+
"app/components/foo/create/create.component.ts",
26+
"app/components/foo/edit/edit.component.html",
27+
"app/components/foo/edit/edit.component.ts",
28+
"app/components/foo/list/list.component.html",
29+
"app/components/foo/list/list.component.ts",
30+
"app/components/foo/show/show.component.html",
31+
"app/components/foo/show/show.component.ts",
32+
"app/app.component.html",
33+
"app/app.component.ts",
34+
35+
//SVG COMPONENT
36+
"app/components/svg/list-svg/list-svg.component.svg",
37+
"app/components/svg/list-svg/list-svg.component.ts",
38+
"app/components/svg/menu/menu.component.svg",
39+
"app/components/svg/menu/menu.component.ts",
40+
41+
//INTERFACE
42+
"app/interface/api.ts",
43+
"app/interface/foo.model.ts",
44+
"app/interface/hero.model.ts",
45+
"app/interface/list.model.ts",
46+
"app/interface/show.model.ts",
47+
"app/interface/update.model.ts",
48+
49+
// ROUTER
50+
"app/router/foo.ts",
51+
"app/app.routes.ts",
52+
53+
//SERVICE
54+
"app/service/hero.service.ts",
55+
]);
56+
57+
handlebars.registerHelper("compare", hbhComparison.compare);
58+
handlebars.registerHelper("lowercase", hbhString.lowercase);
59+
}
60+
61+
generate(api, resource, dir) {
62+
const lc = resource.title.toLowerCase();
63+
const titleUcFirst =
64+
resource.title.charAt(0).toUpperCase() + resource.title.slice(1);
65+
const fields = this.parseFields(resource);
66+
const hasIsRelation = fields.some((field) => field.isRelation);
67+
const hasIsRelations = fields.some((field) => field.isRelations);
68+
const hasDateField = fields.some((field) => field.type === "dateTime");
69+
70+
const context = {
71+
title: resource.title,
72+
name: resource.name,
73+
lc,
74+
uc: resource.title.toUpperCase(),
75+
fields,
76+
formFields: this.buildFields(fields),
77+
hydraPrefix: this.hydraPrefix,
78+
titleUcFirst,
79+
hasIsRelation,
80+
hasIsRelations,
81+
hasRelations: hasIsRelation || hasIsRelations,
82+
hasDateField,
83+
};
84+
85+
//CREATE DIRECTORIES - These directories may already exist
86+
[
87+
`${dir}/assets`,
88+
`${dir}/utils`,
89+
`${dir}/app/components/${lc}`,
90+
`${dir}/app/components/common`,
91+
`${dir}/app/components/svg`,
92+
`${dir}/app/interface`,
93+
`${dir}/app/router`,
94+
`${dir}/app/service`,
95+
].forEach((dir) => this.createDir(dir, false));
96+
97+
//CREATE FILE
98+
[
99+
`${dir}/app/components/svg/list-svg/list-svg.component.svg`,
100+
`${dir}/app/components/svg/list-svg/list-svg.component.ts`,
101+
`${dir}/app/components/svg/menu/menu.component.svg`,
102+
`${dir}/app/components/svg/menu/menu.component.ts`,
103+
`${dir}/app/components/common/delete/delete.component.html`,
104+
`${dir}/app/components/common/delete/delete.component.ts`,
105+
`${dir}/app/components/common/form/form.component.html`,
106+
`${dir}/app/components/common/form/form.component.ts`,
107+
`${dir}/app/components/common/header/header.component.html`,
108+
`${dir}/app/components/common/header/header.component.ts`,
109+
`${dir}/app/components/common/sidebar/sidebar.component.html`,
110+
`${dir}/app/components/common/sidebar/sidebar.component.ts`,
111+
`${dir}/app/components/common/table/table.component.html`,
112+
`${dir}/app/app.component.html`,
113+
`${dir}/app/app.component.ts`,
114+
`${dir}/app/app.routes.ts`,
115+
].forEach((file) => this.createFile(file, file, context, false));
116+
117+
[
118+
`app/components/%s/create/create.component.html",
119+
"app/components/%s/create/create.component.ts",
120+
"app/components/%s/edit/edit.component.html",
121+
"app/components/%s/edit/edit.component.ts",
122+
"app/components/%s/list/list.component.html",
123+
"app/components/%s/list/list.component.ts",
124+
"app/components/%s/show/show.component.html",
125+
"app/components/%s/show/show.component.ts",`,
126+
].forEach((file) => this.createFileFromPattern(file, dir, [lc], context));
127+
}
128+
129+
parseFields(resource) {
130+
const fields = [
131+
...resource.writableFields,
132+
...resource.readableFields,
133+
].reduce((list, field) => {
134+
if (list[field.name]) {
135+
return list;
136+
}
137+
138+
const isReferences = Boolean(
139+
field.reference && field.maxCardinality !== 1
140+
);
141+
const isEmbeddeds = Boolean(field.embedded && field.maxCardinality !== 1);
142+
143+
return {
144+
...list,
145+
[field.name]: {
146+
...field,
147+
isReferences,
148+
isEmbeddeds,
149+
isRelation: field.reference || field.embedded,
150+
isRelations: isEmbeddeds || isReferences,
151+
},
152+
};
153+
}, {});
154+
155+
return Object.values(fields);
156+
}
157+
}
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Api, Resource, Field } from "@api-platform/api-doc-parser";
2+
import path from "path";
3+
import { fileURLToPath } from "url";
4+
import fs from "fs";
5+
import tmp from "tmp";
6+
import AngularGenerator from "./AngularGenerator.js";
7+
8+
const dirname = path.dirname(fileURLToPath(import.meta.url));
9+
10+
test("Generate a React app", () => {
11+
const generator = new AngularGenerator({
12+
hydraPrefix: "hydra:",
13+
templateDirectory: `${dirname}/../../templates`,
14+
});
15+
const tmpobj = tmp.dirSync({ unsafeCleanup: true });
16+
17+
const fields = [
18+
new Field("bar", {
19+
id: "http://schema.org/url",
20+
range: "http://www.w3.org/2001/XMLSchema#string",
21+
reference: null,
22+
required: true,
23+
description: "An URL",
24+
}),
25+
];
26+
const resource = new Resource("abc", "http://example.com/foos", {
27+
id: "abc",
28+
title: "abc",
29+
readableFields: fields,
30+
writableFields: fields,
31+
});
32+
const api = new Api("http://example.com", {
33+
entrypoint: "http://example.com:8080",
34+
title: "My API",
35+
resources: [resource],
36+
});
37+
generator.generate(api, resource, tmpobj.name);
38+
39+
[
40+
"app/components/common/delete/delete.component.html",
41+
"app/components/common/delete/delete.component.ts",
42+
"app/components/common/form/form.component.html",
43+
"app/components/common/form/form.component.ts",
44+
"app/components/common/header/header.component.html",
45+
"app/components/common/header/header.component.ts",
46+
"app/components/common/sidebar/sidebar.component.html",
47+
"app/components/common/sidebar/sidebar.component.ts",
48+
"app/components/common/table /table.component.html",
49+
"app/components/common/table/table.component.ts",
50+
"app/components/svg/list-svg/list-svg.component.svg",
51+
"app/components/svg/list-svg/list-svg.component.ts",
52+
"app/components/svg/menu/menu.component.svg",
53+
"app/components/svg/menu/menu.component.ts",
54+
"app/interface/api.ts",
55+
"app/interface/foo.model.ts",
56+
"app/interface/hero.model.ts",
57+
"app/interface/list.model.ts",
58+
"app/interface/show.model.ts",
59+
"app/interface/update.model.ts",
60+
"app/router/foo.ts",
61+
"app/service/hero.service.ts",
62+
].forEach((file) => expect(fs.existsSync(tmpobj.name + file)).toBe(true));
63+
64+
[
65+
"/components/abc/Form.tsx",
66+
"/components/abc/List.tsx",
67+
"/components/abc/Show.tsx",
68+
"/interfaces/Abc.ts",
69+
].forEach((file) => {
70+
expect(fs.existsSync(tmpobj.name + file)).toBe(true);
71+
expect(fs.readFileSync(tmpobj.name + file, "utf8")).toMatch(/bar/);
72+
});
73+
74+
tmpobj.removeCallback();
75+
});

src/generators/BaseGenerator.js

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export default class {
4444
context,
4545
templateValues = ["foo", "Foo"]
4646
) {
47+
console.log(dir);
4748
this.createFile(
4849
vsprintf(pattern, templateValues),
4950
vsprintf(`${dir}/${pattern}`, values),
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<app-header></app-header>
2+
<div class="flex">
3+
<div class="w-1/6 h-100">
4+
<app-sidebar></app-sidebar>
5+
</div>
6+
<div class="w-5/6 ">
7+
<router-outlet></router-outlet>
8+
</div>
9+
</div>
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {Component} from '@angular/core';
2+
import {RouterOutlet} from '@angular/router';
3+
import {AsyncPipe} from "@angular/common";
4+
import {HttpClientModule} from "@angular/common/http";
5+
import {SidebarComponent} from "./components/common/sidebar/sidebar.component";
6+
import {HeaderComponent} from "./components/common/header/header.component";
7+
8+
@Component({
9+
selector: 'app-root',
10+
standalone: true,
11+
imports: [RouterOutlet, AsyncPipe, HttpClientModule, SidebarComponent, HeaderComponent],
12+
templateUrl: './app.component.html',
13+
styleUrl: './app.component.css'
14+
})
15+
export class AppComponent {
16+
title = 'Api Platform Angular Admin';
17+
}

templates/angular/app/app.routes.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {Routes} from '@angular/router';
2+
import {ListComponent} from "./components/foo/list/list.component";
3+
import {ShowComponent} from "./components/foo/show/show.component";
4+
import {EditComponent} from "./components/foo/edit/edit.component";
5+
import {CreateComponent} from "./components/foo/create/create.component";
6+
7+
export const routes: Routes = [
8+
{
9+
path: 'heroes',
10+
component: ListComponent
11+
},
12+
{
13+
path: 'heroes/add',
14+
component: CreateComponent
15+
},
16+
{
17+
path: 'heroes/:id',
18+
component: ShowComponent,
19+
},
20+
{
21+
path: 'heroes/:id/edit',
22+
component: EditComponent
23+
},
24+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<button
2+
(click)="deleteAction()"
3+
[className]="!disabled ?
4+
'py-2 px-4 bg-red-600 text-white text-sm rounded shadow-md hover:bg-red-700' :
5+
'py-2 px-4 bg-red-600 text-white text-sm rounded shadow-md opacity-25'"
6+
[disabled]="disabled">
7+
Delete
8+
</button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {Component, EventEmitter, Input, Output} from '@angular/core';
2+
import {HeroService} from "../../../service/hero.service";
3+
import {Location} from "@angular/common";
4+
5+
@Component({
6+
selector: 'app-delete',
7+
standalone: true,
8+
imports: [],
9+
templateUrl: './delete.component.html',
10+
})
11+
export class DeleteComponent {
12+
@Input() disabled!: boolean
13+
@Output() delete: EventEmitter<Function> = new EventEmitter<Function>()
14+
constructor(
15+
private heroService: HeroService,
16+
private location: Location
17+
) {
18+
}
19+
20+
deleteAction () {
21+
return this.delete.emit()
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>form works!</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-form',
5+
standalone: true,
6+
imports: [],
7+
templateUrl: './form.component.html',
8+
})
9+
export class FormComponent {
10+
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.header {
2+
background-color: #288690;
3+
width: 100%;
4+
padding: 0 8px;
5+
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2),
6+
0 4px 5px 0 rgba(0, 0, 0, 0.14),
7+
0 1px 10px 0 rgba(0, 0, 0, 0.12);
8+
}
9+
10+
.menu {
11+
padding: 8px;
12+
cursor: pointer;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<header class="header">
2+
<div class="menu">
3+
<app-menu />
4+
</div>
5+
</header>

0 commit comments

Comments
 (0)