@sealcode/sealgen 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nyc_output/1c5dff80-7861-427a-b1bb-d0686211f098.json +1 -0
- package/.nyc_output/a38a0d08-1f1f-4973-b5e2-efc434c1593d.json +1 -0
- package/.nyc_output/processinfo/1c5dff80-7861-427a-b1bb-d0686211f098.json +1 -0
- package/.nyc_output/processinfo/a38a0d08-1f1f-4973-b5e2-efc434c1593d.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.xunit +3 -0
- package/@types/forms/controls/field-group.d.ts +17 -0
- package/@types/forms/controls/form-control.d.ts +1 -1
- package/@types/forms/controls/form-field-control.d.ts +1 -1
- package/@types/forms/controls/simple-input.d.ts +2 -1
- package/@types/forms/form.d.ts +19 -6
- package/@types/forms/multiform.d.ts +9 -11
- package/@types/index.d.ts +1 -0
- package/@types/page/mountable-with-fields.d.ts +32 -0
- package/@types/page/mountable.d.ts +2 -27
- package/@types/page/page.d.ts +2 -2
- package/@types/templates/multiform.d.ts +1 -0
- package/@types/templates/redirect.d.ts +1 -0
- package/@types/utils/input-wrapper.d.ts +4 -0
- package/coverage/clover.xml +79 -0
- package/lib/add-route.js +27 -8
- package/lib/add-route.js.map +1 -1
- package/lib/forms/controls/checkboxed-list-input.js +12 -9
- package/lib/forms/controls/checkboxed-list-input.js.map +1 -1
- package/lib/forms/controls/dropdown.js +14 -12
- package/lib/forms/controls/dropdown.js.map +1 -1
- package/lib/forms/controls/field-group.js +26 -0
- package/lib/forms/controls/field-group.js.map +1 -0
- package/lib/forms/controls/form-control.js.map +1 -1
- package/lib/forms/controls/form-field-control.js.map +1 -1
- package/lib/forms/controls/simple-input.js +6 -8
- package/lib/forms/controls/simple-input.js.map +1 -1
- package/lib/forms/controls/textarea.js +13 -15
- package/lib/forms/controls/textarea.js.map +1 -1
- package/lib/forms/form.js +95 -57
- package/lib/forms/form.js.map +1 -1
- package/lib/forms/multiform.js +52 -62
- package/lib/forms/multiform.js.map +1 -1
- package/lib/generate-routes.js +7 -3
- package/lib/generate-routes.js.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/page/list.js +1 -1
- package/lib/page/list.js.map +1 -1
- package/lib/page/mountable-with-fields.js +63 -0
- package/lib/page/mountable-with-fields.js.map +1 -0
- package/lib/page/mountable.js +2 -49
- package/lib/page/mountable.js.map +1 -1
- package/lib/page/page.js +2 -2
- package/lib/page/page.js.map +1 -1
- package/lib/templates/multiform.js +59 -0
- package/lib/templates/multiform.js.map +1 -0
- package/lib/templates/redirect.js +27 -0
- package/lib/templates/redirect.js.map +1 -0
- package/lib/tests/manual/multiform.js +14 -5
- package/lib/tests/manual/multiform.js.map +1 -1
- package/lib/utils/input-wrapper.js +20 -0
- package/lib/utils/input-wrapper.js.map +1 -0
- package/package.json +4 -3
- package/src/add-route.ts +30 -11
- package/src/forms/controls/checkboxed-list-input.ts +17 -10
- package/src/forms/controls/dropdown.ts +28 -23
- package/src/forms/controls/form-control.ts +3 -1
- package/src/forms/controls/form-field-control.ts +4 -1
- package/src/forms/controls/simple-input.ts +19 -12
- package/src/forms/controls/textarea.ts +15 -14
- package/src/forms/form.ts +122 -76
- package/src/forms/multiform.ts +211 -0
- package/src/generate-routes.ts +7 -3
- package/src/index.ts +1 -0
- package/src/page/list.ts +1 -1
- package/src/page/mountable-with-fields.ts +112 -0
- package/src/page/mountable.ts +4 -96
- package/src/page/page.ts +7 -3
- package/src/templates/multiform.ts +58 -0
- package/src/templates/redirect.ts +22 -0
- package/src/tests/manual/multiform.ts +106 -0
- package/src/utils/input-wrapper.ts +18 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Context } from "koa";
|
|
2
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
3
|
+
import { FormControl } from "../forms/controls/form-control";
|
|
4
|
+
import { FormField } from "../forms/fields/field";
|
|
5
|
+
import { FormData, FormDataValue, FormMessage } from "../forms/form";
|
|
6
|
+
import { Mountable } from "./mountable";
|
|
7
|
+
|
|
8
|
+
export type PageErrorMessage = { type: "access" | "internal"; message: string };
|
|
9
|
+
|
|
10
|
+
export type Fields = Record<string, FormField<unknown>>;
|
|
11
|
+
|
|
12
|
+
export abstract class MountableWithFields<
|
|
13
|
+
F extends Fields = Fields
|
|
14
|
+
> extends Mountable {
|
|
15
|
+
fields: F;
|
|
16
|
+
field_names_prefix = ""; // useful for multiform, where many forms are merged into one and field assignment is made using the prefix
|
|
17
|
+
form_id = ""; // all fields within this mountable will be tied to form of this id
|
|
18
|
+
abstract controls: FormControl[];
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
if (!this.fields) this.fields = {} as F;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
init(): void {
|
|
26
|
+
for (const [name, field] of Object.entries(this.fields)) {
|
|
27
|
+
field.init(name);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// this one is meant to be overwritten
|
|
32
|
+
async validateValues(
|
|
33
|
+
ctx: Context,
|
|
34
|
+
data: Record<string, FormDataValue>
|
|
35
|
+
): Promise<{ valid: boolean; error: string }> {
|
|
36
|
+
return {
|
|
37
|
+
valid: true,
|
|
38
|
+
error: "",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async validate(
|
|
43
|
+
ctx: Context,
|
|
44
|
+
values: Record<string, FormDataValue>
|
|
45
|
+
): Promise<{
|
|
46
|
+
valid: boolean;
|
|
47
|
+
field_errors: Partial<Record<keyof Fields, string>>;
|
|
48
|
+
form_messages: FormMessage[];
|
|
49
|
+
}> {
|
|
50
|
+
const field_errors = {} as Record<keyof Fields, string>;
|
|
51
|
+
let valid = true;
|
|
52
|
+
const form_messages = [] as FormMessage[];
|
|
53
|
+
await Promise.all(
|
|
54
|
+
Object.entries(values)
|
|
55
|
+
.filter(([fieldname]) => fieldname in this.fields)
|
|
56
|
+
.map(async ([key]: [keyof F, FormDataValue]) => {
|
|
57
|
+
const field = this.fields[key];
|
|
58
|
+
const { valid: fieldvalid, message: fieldmessage } =
|
|
59
|
+
await field.getValue(ctx, values);
|
|
60
|
+
if (!fieldvalid) {
|
|
61
|
+
valid = false;
|
|
62
|
+
field_errors[field.name] = fieldmessage;
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
const formValidationResult = await this.validateValues(ctx, values);
|
|
67
|
+
if (!formValidationResult.valid) {
|
|
68
|
+
form_messages.push({
|
|
69
|
+
type: "error",
|
|
70
|
+
text: formValidationResult.error,
|
|
71
|
+
});
|
|
72
|
+
valid = false;
|
|
73
|
+
}
|
|
74
|
+
return { valid, field_errors, form_messages };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public renderControls(
|
|
78
|
+
ctx: Context,
|
|
79
|
+
data: FormData<keyof Fields>
|
|
80
|
+
): FlatTemplatable {
|
|
81
|
+
return tempstream/* HTML */ `${this.controls.map((control) =>
|
|
82
|
+
control.render(
|
|
83
|
+
ctx,
|
|
84
|
+
data.raw_values,
|
|
85
|
+
data.messages,
|
|
86
|
+
this.field_names_prefix,
|
|
87
|
+
this.form_id
|
|
88
|
+
)
|
|
89
|
+
)}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async renderError(
|
|
93
|
+
_: Context,
|
|
94
|
+
error: PageErrorMessage
|
|
95
|
+
): Promise<FlatTemplatable> {
|
|
96
|
+
return error.message;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public renderMessages(
|
|
100
|
+
_: Context,
|
|
101
|
+
data: FormData<keyof Fields>
|
|
102
|
+
): FlatTemplatable {
|
|
103
|
+
return tempstream/* HTML */ `<div class="form-messages">
|
|
104
|
+
${data.messages.map(
|
|
105
|
+
(message) =>
|
|
106
|
+
`<div class="form-message form-message--${message.type}">${message.text}</div>`
|
|
107
|
+
)}
|
|
108
|
+
</div>`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
abstract extractRawValues(ctx: Context): Record<string, FormDataValue>;
|
|
112
|
+
}
|
package/src/page/mountable.ts
CHANGED
|
@@ -1,105 +1,13 @@
|
|
|
1
1
|
import Router from "@koa/router";
|
|
2
|
-
import { FormControl } from "../forms/controls/form-control";
|
|
3
|
-
import { Context } from "koa";
|
|
4
|
-
import { FlatTemplatable, tempstream } from "tempstream";
|
|
5
|
-
import { FormField } from "../forms/fields/field";
|
|
6
|
-
import { FormData, FormDataValue, FormMessage } from "../forms/form";
|
|
7
|
-
|
|
8
|
-
export type PageErrorMessage = { type: "access" | "internal"; message: string };
|
|
9
|
-
|
|
10
|
-
export type Fields = Record<string, FormField<unknown>>;
|
|
11
2
|
|
|
12
|
-
|
|
13
|
-
fields: F;
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
if (!this.fields) this.fields = {} as F;
|
|
17
|
-
}
|
|
3
|
+
import { Context } from "koa";
|
|
18
4
|
|
|
19
|
-
|
|
5
|
+
export abstract class Mountable {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
7
|
+
init(): void {}
|
|
20
8
|
abstract mount(router: Router, path: string): void;
|
|
21
9
|
|
|
22
10
|
abstract canAccess(
|
|
23
11
|
ctx: Context
|
|
24
12
|
): Promise<{ canAccess: boolean; message: string }>;
|
|
25
|
-
|
|
26
|
-
abstract renderError(
|
|
27
|
-
ctx: Context,
|
|
28
|
-
error: PageErrorMessage
|
|
29
|
-
): Promise<FlatTemplatable>;
|
|
30
|
-
|
|
31
|
-
abstract extractRawValues(ctx: Context): Record<string, FormDataValue>;
|
|
32
|
-
|
|
33
|
-
// this one is meant to be overwritten
|
|
34
|
-
async validateValues(
|
|
35
|
-
ctx: Context,
|
|
36
|
-
data: Record<string, FormDataValue>
|
|
37
|
-
): Promise<{ valid: boolean; error: string }> {
|
|
38
|
-
return {
|
|
39
|
-
valid: true,
|
|
40
|
-
error: "",
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
protected async validate(
|
|
45
|
-
ctx: Context,
|
|
46
|
-
values: Record<string, FormDataValue>
|
|
47
|
-
): Promise<{
|
|
48
|
-
valid: boolean;
|
|
49
|
-
field_errors: Partial<Record<keyof Fields, string>>;
|
|
50
|
-
form_messages: FormMessage[];
|
|
51
|
-
}> {
|
|
52
|
-
const field_errors = {} as Record<keyof Fields, string>;
|
|
53
|
-
let valid = true;
|
|
54
|
-
const form_messages = [] as FormMessage[];
|
|
55
|
-
await Promise.all(
|
|
56
|
-
Object.entries(values)
|
|
57
|
-
.filter(([fieldname]) => fieldname in this.fields)
|
|
58
|
-
.map(async ([key]: [keyof F, FormDataValue]) => {
|
|
59
|
-
const field = this.fields[key];
|
|
60
|
-
const { valid: fieldvalid, message: fieldmessage } =
|
|
61
|
-
await field.getValue(ctx, values);
|
|
62
|
-
if (!fieldvalid) {
|
|
63
|
-
valid = false;
|
|
64
|
-
field_errors[field.name] = fieldmessage;
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
const formValidationResult = await this.validateValues(ctx, values);
|
|
69
|
-
if (!formValidationResult.valid) {
|
|
70
|
-
form_messages.push({
|
|
71
|
-
type: "error",
|
|
72
|
-
text: formValidationResult.error,
|
|
73
|
-
});
|
|
74
|
-
valid = false;
|
|
75
|
-
}
|
|
76
|
-
return { valid, field_errors, form_messages };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
init(): void {
|
|
80
|
-
for (const [name, field] of Object.entries(this.fields)) {
|
|
81
|
-
field.init(name);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public renderControls(
|
|
86
|
-
ctx: Context,
|
|
87
|
-
data: FormData<keyof Fields>
|
|
88
|
-
): FlatTemplatable {
|
|
89
|
-
return tempstream/* HTML */ `${this.controls.map((control) =>
|
|
90
|
-
control.render(ctx, data.raw_values, data.messages)
|
|
91
|
-
)}`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public renderMessages(
|
|
95
|
-
_: Context,
|
|
96
|
-
data: FormData<keyof Fields>
|
|
97
|
-
): FlatTemplatable {
|
|
98
|
-
return tempstream/* HTML */ `<div class="form-messages">
|
|
99
|
-
${data.messages.map(
|
|
100
|
-
(message) =>
|
|
101
|
-
`<div class="form-message form-message--${message.type}">${message.text}</div>`
|
|
102
|
-
)}
|
|
103
|
-
</div>`;
|
|
104
|
-
}
|
|
105
13
|
}
|
package/src/page/page.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import Router from "@koa/router";
|
|
2
2
|
import { Context } from "koa";
|
|
3
3
|
|
|
4
|
-
import { FlatTemplatable,
|
|
4
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
5
5
|
import { AllQueryParams } from "..";
|
|
6
6
|
import { FormDataValue } from "../forms/form";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Fields,
|
|
9
|
+
MountableWithFields,
|
|
10
|
+
PageErrorMessage,
|
|
11
|
+
} from "./mountable-with-fields";
|
|
8
12
|
import { PagePropsParser } from "./props-parser";
|
|
9
13
|
|
|
10
14
|
export abstract class Page<
|
|
11
15
|
F extends Fields = Record<string, never>
|
|
12
|
-
> extends
|
|
16
|
+
> extends MountableWithFields<F> {
|
|
13
17
|
propsParser: PagePropsParser<Record<keyof Fields, unknown>> =
|
|
14
18
|
new AllQueryParams();
|
|
15
19
|
controls = [];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { curryImportPath } from "../utils/utils";
|
|
2
|
+
|
|
3
|
+
export async function multiformTemplate(
|
|
4
|
+
action_name: string,
|
|
5
|
+
newfilefullpath: string
|
|
6
|
+
): Promise<string> {
|
|
7
|
+
const rel = curryImportPath(newfilefullpath);
|
|
8
|
+
return `import { Context } from "koa";
|
|
9
|
+
import html from "${rel("src/back/html")}";
|
|
10
|
+
import { Controls, Fields, Form, FormMessage, Multiform } from "@sealcode/sealgen";
|
|
11
|
+
import { FlatTemplatable } from "tempstream";
|
|
12
|
+
|
|
13
|
+
export const actionName = "${action_name}";
|
|
14
|
+
|
|
15
|
+
const fields1 = {
|
|
16
|
+
email: new Fields.SimpleFormField(true),
|
|
17
|
+
password: new Fields.SimpleFormField(true),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const form1 = new (class extends Form<typeof fields1> {
|
|
21
|
+
defaultSuccessMessage = "Form1 done!";
|
|
22
|
+
controls = [
|
|
23
|
+
new Controls.FormHeader("form1"),
|
|
24
|
+
new Controls.SimpleInput(fields1.email),
|
|
25
|
+
];
|
|
26
|
+
fields = fields1;
|
|
27
|
+
onSubmit() {
|
|
28
|
+
console.log("form1 submit");
|
|
29
|
+
}
|
|
30
|
+
})();
|
|
31
|
+
|
|
32
|
+
const fields2 = {
|
|
33
|
+
name: new Fields.SimpleFormField(true),
|
|
34
|
+
surname: new Fields.SimpleFormField(true),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const form2 = new (class extends Form<Record<never, unknown>> {
|
|
38
|
+
defaultSuccessMessage = "Form2 done!";
|
|
39
|
+
controls = [new Controls.FormHeader("form2"), new Controls.SimpleInput(fields2.name)];
|
|
40
|
+
fields = fields2;
|
|
41
|
+
onSubmit() {
|
|
42
|
+
console.log("form2 submit");
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
|
|
46
|
+
export default new (class extends Multiform {
|
|
47
|
+
name = "multiform_test";
|
|
48
|
+
forms = { form1, form2 };
|
|
49
|
+
async render(
|
|
50
|
+
ctx: Context,
|
|
51
|
+
messages: FormMessage[],
|
|
52
|
+
prerenderedForms?: Record<string, FlatTemplatable | undefined>
|
|
53
|
+
): Promise<FlatTemplatable> {
|
|
54
|
+
return html(ctx, "${action_name}", await super.render(ctx, messages, prerenderedForms));
|
|
55
|
+
}
|
|
56
|
+
})();
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export async function redirectTemplate(action_name: string): Promise<string> {
|
|
2
|
+
return `import { Context } from "koa";
|
|
3
|
+
import { Mountable } from "@sealcode/sealgen";
|
|
4
|
+
import Router from "@koa/router";
|
|
5
|
+
|
|
6
|
+
export const actionName = "${action_name}";
|
|
7
|
+
|
|
8
|
+
export default new (class ${action_name}Redirect extends Mountable {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
10
|
+
async canAccess(_: Context) {
|
|
11
|
+
return { canAccess: true, message: "" };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
mount(router: Router, path: string) {
|
|
15
|
+
router.get(path, async (ctx) => {
|
|
16
|
+
ctx.status = 302;
|
|
17
|
+
ctx.redirect("/new_url");
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
})();
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import Koa, { Context } from "koa";
|
|
2
|
+
import Router from "@koa/router";
|
|
3
|
+
import { Controls, Fields, Form } from "../..";
|
|
4
|
+
import { Multiform } from "../../forms/multiform";
|
|
5
|
+
import koaBody from "koa-body";
|
|
6
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
7
|
+
import { App } from "sealious";
|
|
8
|
+
import { FormMessage } from "../../forms/form";
|
|
9
|
+
const app = new Koa({ proxy: true });
|
|
10
|
+
const router = new Router();
|
|
11
|
+
|
|
12
|
+
const fields1 = {
|
|
13
|
+
email: new Fields.SimpleFormField(true),
|
|
14
|
+
password: new Fields.SimpleFormField(true),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const form1 = new (class extends Form<typeof fields1> {
|
|
18
|
+
defaultSuccessMessage = "Form1 done!";
|
|
19
|
+
controls = [
|
|
20
|
+
new Controls.FormHeader("form1"),
|
|
21
|
+
new Controls.SimpleInput(fields1.email),
|
|
22
|
+
];
|
|
23
|
+
fields = fields1;
|
|
24
|
+
onSubmit() {
|
|
25
|
+
console.log("form1 submit");
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
28
|
+
|
|
29
|
+
const fields2 = {
|
|
30
|
+
name: new Fields.SimpleFormField(true),
|
|
31
|
+
surname: new Fields.SimpleFormField(true),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const form2 = new (class extends Form<Record<never, unknown>> {
|
|
35
|
+
defaultSuccessMessage = "Form2 done!";
|
|
36
|
+
controls = [
|
|
37
|
+
new Controls.FormHeader("form2"),
|
|
38
|
+
new Controls.SimpleInput(fields2.name),
|
|
39
|
+
];
|
|
40
|
+
fields = fields2;
|
|
41
|
+
onSubmit() {
|
|
42
|
+
console.log("form2 submit");
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
|
|
46
|
+
const multiform = new (class extends Multiform {
|
|
47
|
+
name = "multiform_test";
|
|
48
|
+
forms = { form1, form2 };
|
|
49
|
+
async render(
|
|
50
|
+
ctx: Koa.Context,
|
|
51
|
+
messages: FormMessage[],
|
|
52
|
+
prerenderedForms?: Record<string, FlatTemplatable | undefined>
|
|
53
|
+
): Promise<FlatTemplatable> {
|
|
54
|
+
return tempstream`<!DOCTYPE html>
|
|
55
|
+
<html>
|
|
56
|
+
<head>
|
|
57
|
+
<title>Multiform Test - Sealgen</title>
|
|
58
|
+
<script type="module">
|
|
59
|
+
import hotwiredTurbo from "https://cdn.skypack.dev/pin/@hotwired/turbo@v7.0.0-9XNv1By6uiqRlBzQLO2d/mode=imports,min/optimized/@hotwired/turbo.js";
|
|
60
|
+
</script>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
${super.render(ctx, messages, prerenderedForms)}
|
|
64
|
+
</body>
|
|
65
|
+
</html>`;
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
|
|
69
|
+
const URL = "/";
|
|
70
|
+
|
|
71
|
+
async function handleHtmlPromise(ctx: Context, next: Koa.Next) {
|
|
72
|
+
await next();
|
|
73
|
+
if (ctx.body instanceof Promise) {
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
75
|
+
ctx.body = await ctx.body;
|
|
76
|
+
}
|
|
77
|
+
ctx.set("content-type", "text/html;charset=utf-8");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
router.use(
|
|
81
|
+
URL,
|
|
82
|
+
koaBody(),
|
|
83
|
+
async (ctx, next) => {
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
85
|
+
ctx.$body = ctx.request.body;
|
|
86
|
+
ctx.$app = {
|
|
87
|
+
getString: (
|
|
88
|
+
_: unknown,
|
|
89
|
+
args: unknown[],
|
|
90
|
+
trans: string | ((...args: unknown[]) => string)
|
|
91
|
+
) => (typeof trans == "function" ? trans(args) : trans),
|
|
92
|
+
} as unknown as App;
|
|
93
|
+
await next();
|
|
94
|
+
},
|
|
95
|
+
handleHtmlPromise
|
|
96
|
+
);
|
|
97
|
+
multiform.init();
|
|
98
|
+
multiform.mount(router, URL);
|
|
99
|
+
// to automatically add trailing slashes:
|
|
100
|
+
|
|
101
|
+
router.use(URL, (ctx) => ctx.set("content-type", "text/html;charset=utf-8"));
|
|
102
|
+
|
|
103
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
104
|
+
app.listen(3000);
|
|
105
|
+
|
|
106
|
+
console.log("Multiform test app listening on localhost:3000");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Readable } from "stream";
|
|
2
|
+
import { tempstream } from "tempstream";
|
|
3
|
+
|
|
4
|
+
export function getRequiredClass(isRequired: boolean): string {
|
|
5
|
+
return isRequired ? "required" : "";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function getModifierClasses(modifiers: string[]) {
|
|
9
|
+
return modifiers.map((m) => "form-input__wrapper--" + m).join(" ");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function inputWrapper(modifiers: string[], input: string): Readable {
|
|
13
|
+
return tempstream/* HTML */ `<div
|
|
14
|
+
class="form-input form-input__wrapper ${getModifierClasses(modifiers)}"
|
|
15
|
+
>
|
|
16
|
+
${input}
|
|
17
|
+
</div>`;
|
|
18
|
+
}
|