@sealcode/sealgen 0.2.1 → 0.3.2
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/.eslintrc.js +1 -0
- package/@types/build.d.ts +1 -0
- package/@types/cli.d.ts +2 -0
- package/@types/forms/controls/checkboxed-list-input.d.ts +16 -0
- package/@types/forms/controls/controls.d.ts +9 -0
- package/@types/forms/controls/dropdown.d.ts +20 -0
- package/@types/forms/controls/editable-collection-subset.d.ts +21 -0
- package/@types/forms/controls/form-control.d.ts +8 -0
- package/@types/forms/controls/form-field-control.d.ts +13 -0
- package/@types/forms/controls/form-header.d.ts +9 -0
- package/@types/forms/controls/form-paragraph.d.ts +7 -0
- package/@types/forms/controls/frame.d.ts +17 -0
- package/@types/forms/controls/simple-input.d.ts +28 -0
- package/@types/forms/controls.d.ts +0 -0
- package/@types/forms/field.d.ts +42 -0
- package/@types/forms/form-fields-list.d.ts +4 -0
- package/@types/forms/form.d.ts +42 -0
- package/@types/forms/validator.d.ts +3 -0
- package/@types/index.d.ts +15 -2
- package/@types/page/access-control.d.ts +6 -0
- package/@types/page/list.d.ts +59 -0
- package/@types/page/page.d.ts +26 -0
- package/@types/page/props-parser.d.ts +20 -0
- package/@types/run.d.ts +0 -0
- package/@types/templates/page.d.ts +1 -1
- package/@types/utils/natural-numbers.d.ts +1 -0
- package/@types/utils/sleep.d.ts +1 -0
- package/@types/utils/url-with-new-params.d.ts +2 -0
- package/@types/utils/utils.d.ts +9 -0
- package/@types/utils/walk.d.ts +1 -1
- package/lib/build.js +116 -0
- package/lib/build.js.map +1 -0
- package/lib/cli.js +30 -0
- package/lib/cli.js.map +1 -0
- package/lib/find-scss-includes.js +2 -1
- package/lib/find-scss-includes.js.map +1 -1
- package/lib/forms/controls/checkboxed-list-input.js +43 -0
- package/lib/forms/controls/checkboxed-list-input.js.map +1 -0
- package/lib/forms/controls/controls.js +22 -0
- package/lib/forms/controls/controls.js.map +1 -0
- package/lib/forms/controls/dropdown.js +40 -0
- package/lib/forms/controls/dropdown.js.map +1 -0
- package/lib/forms/controls/editable-collection-subset.js +73 -0
- package/lib/forms/controls/editable-collection-subset.js.map +1 -0
- package/lib/forms/controls/form-control.js +7 -0
- package/lib/forms/controls/form-control.js.map +1 -0
- package/lib/forms/controls/form-field-control.js +22 -0
- package/lib/forms/controls/form-field-control.js.map +1 -0
- package/lib/forms/controls/form-header.js +18 -0
- package/lib/forms/controls/form-header.js.map +1 -0
- package/lib/forms/controls/form-paragraph.js +16 -0
- package/lib/forms/controls/form-paragraph.js.map +1 -0
- package/lib/forms/controls/frame.js +29 -0
- package/lib/forms/controls/frame.js.map +1 -0
- package/lib/forms/controls/simple-input.js +50 -0
- package/lib/forms/controls/simple-input.js.map +1 -0
- package/lib/forms/controls.js +1 -0
- package/lib/forms/controls.js.map +1 -0
- package/lib/forms/field.js +80 -0
- package/lib/forms/field.js.map +1 -0
- package/lib/forms/form-fields-list.js +10 -0
- package/lib/forms/form-fields-list.js.map +1 -0
- package/lib/forms/form.js +162 -0
- package/lib/forms/form.js.map +1 -0
- package/lib/forms/validator.js +11 -0
- package/lib/forms/validator.js.map +1 -0
- package/lib/generate-routes.js +11 -4
- package/lib/generate-routes.js.map +1 -1
- package/lib/index.js +18 -25
- package/lib/index.js.map +1 -1
- package/lib/page/access-control.js +20 -0
- package/lib/page/access-control.js.map +1 -0
- package/lib/page/list.js +148 -0
- package/lib/page/list.js.map +1 -0
- package/lib/page/page.js +20 -0
- package/lib/page/page.js.map +1 -0
- package/lib/page/props-parser.js +74 -0
- package/lib/page/props-parser.js.map +1 -0
- package/lib/run.js +1 -0
- package/lib/run.js.map +1 -0
- package/lib/templates/form.js +10 -5
- package/lib/templates/form.js.map +1 -1
- package/lib/templates/list.js +3 -3
- package/lib/templates/list.js.map +1 -1
- package/lib/templates/page.js +5 -4
- package/lib/templates/page.js.map +1 -1
- package/lib/utils/escape-url-params.js +1 -0
- package/lib/utils/escape-url-params.js.map +1 -1
- package/lib/utils/list-collections.js +1 -1
- package/lib/utils/list-collections.js.map +1 -1
- package/lib/utils/natural-numbers.js +10 -0
- package/lib/utils/natural-numbers.js.map +1 -0
- package/lib/utils/sleep.js +8 -0
- package/lib/utils/sleep.js.map +1 -0
- package/lib/utils/url-with-new-params.js +12 -0
- package/lib/utils/url-with-new-params.js.map +1 -0
- package/lib/utils/utils.js +19 -0
- package/lib/utils/utils.js.map +1 -0
- package/lib/utils/walk.js +4 -4
- package/lib/utils/walk.js.map +1 -1
- package/package.json +17 -4
- package/src/build.ts +92 -0
- package/src/cli.ts +34 -0
- package/src/find-scss-includes.ts +3 -2
- package/src/forms/controls/checkboxed-list-input.ts +50 -0
- package/src/forms/controls/controls.ts +9 -0
- package/src/forms/controls/dropdown.ts +57 -0
- package/src/forms/controls/editable-collection-subset.ts +91 -0
- package/src/forms/controls/form-control.ts +13 -0
- package/src/forms/controls/form-field-control.ts +39 -0
- package/src/forms/controls/form-header.ts +16 -0
- package/src/forms/controls/form-paragraph.ts +11 -0
- package/src/forms/controls/frame.ts +26 -0
- package/src/forms/controls/simple-input.ts +73 -0
- package/src/forms/controls.ts +0 -0
- package/src/forms/field.ts +119 -0
- package/src/forms/form-fields-list.ts +7 -0
- package/src/forms/form.ts +242 -0
- package/src/forms/forms.scss +18 -0
- package/src/forms/validator.ts +10 -0
- package/src/generate-routes.ts +12 -9
- package/src/index.ts +15 -28
- package/src/page/access-control.ts +18 -0
- package/src/page/list.ts +214 -0
- package/src/page/page.ts +31 -0
- package/src/page/props-parser.ts +99 -0
- package/src/run.ts +0 -0
- package/src/templates/form.ts +10 -5
- package/src/templates/list.ts +3 -3
- package/src/templates/page.ts +6 -6
- package/src/utils/escape-url-params.ts +1 -0
- package/src/utils/list-collections.ts +2 -2
- package/src/utils/natural-numbers.ts +5 -0
- package/src/utils/sleep.ts +3 -0
- package/src/utils/url-with-new-params.ts +9 -0
- package/src/utils/utils.ts +10 -0
- package/src/utils/walk.ts +2 -2
- package/tsconfig.json +5 -2
- package/.log/ti-101379.log +0 -524
- package/.log/ti-131883.log +0 -505
- package/.log/ti-13490.log +0 -652
- package/.log/ti-138140.log +0 -505
- package/.log/ti-138288.log +0 -526
- package/.log/ti-46589.log +0 -675
- package/.log/tsserver.log +0 -1666
- package/src/tsconfig.json +0 -18
package/src/page/list.ts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { BaseContext } from "koa";
|
|
2
|
+
import { Collection, CollectionItem } from "sealious";
|
|
3
|
+
import { Templatable, tempstream } from "tempstream";
|
|
4
|
+
import { peopleWhoCan } from "./access-control";
|
|
5
|
+
import { Page } from "./page";
|
|
6
|
+
import { predicates, ShapeToType } from "@sealcode/ts-predicates";
|
|
7
|
+
import { PagePropsParser } from "./props-parser";
|
|
8
|
+
import { FormFieldControl } from "../forms/controls/controls";
|
|
9
|
+
import { FormField } from "../forms/field";
|
|
10
|
+
import { FormData } from "../forms/form";
|
|
11
|
+
import { FormFieldsList } from "../forms/form-fields-list";
|
|
12
|
+
import { naturalNumbers, UrlWithNewParams } from "../utils/utils";
|
|
13
|
+
|
|
14
|
+
export const BasePagePropsShape = <const>{};
|
|
15
|
+
export type BasePageProps = ShapeToType<typeof BasePagePropsShape>;
|
|
16
|
+
|
|
17
|
+
export const BaseListPagePropsShape = <const>{
|
|
18
|
+
page: predicates.number,
|
|
19
|
+
itemsPerPage: predicates.number,
|
|
20
|
+
};
|
|
21
|
+
export type BaseListPageProps = ShapeToType<typeof BaseListPagePropsShape>;
|
|
22
|
+
export const BaseListPageDefaultProps = { page: 1, itemsPerPage: 25 };
|
|
23
|
+
|
|
24
|
+
export type PropsErrors<PropsType> = Partial<Record<keyof PropsType, string>>;
|
|
25
|
+
|
|
26
|
+
export abstract class ListPage<
|
|
27
|
+
ItemType,
|
|
28
|
+
PropsType extends BaseListPageProps = BaseListPageProps
|
|
29
|
+
> extends Page {
|
|
30
|
+
abstract getItems(
|
|
31
|
+
ctx: BaseContext,
|
|
32
|
+
props: PropsType
|
|
33
|
+
): Promise<{ items: ItemType[] }>;
|
|
34
|
+
abstract getTotalPages(ctx: BaseContext, props: PropsType): Promise<number>;
|
|
35
|
+
abstract renderItem(ctx: BaseContext, item: ItemType): Promise<Templatable>;
|
|
36
|
+
abstract propsParser: PagePropsParser<PropsType>;
|
|
37
|
+
|
|
38
|
+
filterFields: FormField<keyof PropsType>[] = [];
|
|
39
|
+
filterControls: FormFieldControl[] = [];
|
|
40
|
+
|
|
41
|
+
renderListContainer(_: BaseContext, content: Templatable): Templatable {
|
|
42
|
+
return tempstream`<div>${content}</div>`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async validateProps(
|
|
46
|
+
ctx: BaseContext,
|
|
47
|
+
props: PropsType
|
|
48
|
+
): Promise<{ valid: boolean; errors: PropsErrors<PropsType> }> {
|
|
49
|
+
const errors: PropsErrors<PropsType> = {};
|
|
50
|
+
let has_errors = false;
|
|
51
|
+
const promises = [];
|
|
52
|
+
for (const [key, value] of Object.entries(props)) {
|
|
53
|
+
const field = FormFieldsList.getField(this.filterFields, key);
|
|
54
|
+
if (field) {
|
|
55
|
+
promises.push(
|
|
56
|
+
field._validate(ctx, value).then(({ valid, message }) => {
|
|
57
|
+
if (!valid) {
|
|
58
|
+
has_errors = true;
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
60
|
+
errors[key as keyof PropsType] = message;
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
await Promise.all(promises);
|
|
67
|
+
return { valid: has_errors, errors };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getProps(ctx: BaseContext): Promise<{
|
|
71
|
+
parsed_props: PropsType;
|
|
72
|
+
errors: PropsErrors<PropsType>;
|
|
73
|
+
raw_props: PropsType;
|
|
74
|
+
}> {
|
|
75
|
+
const raw_props = this.propsParser.decode(ctx);
|
|
76
|
+
const parsed_props = { ...raw_props };
|
|
77
|
+
const { errors } = await this.validateProps(ctx, parsed_props);
|
|
78
|
+
for (const prop_name in errors) {
|
|
79
|
+
const default_value = this.propsParser.getDefaultValue(prop_name);
|
|
80
|
+
if (default_value !== undefined) {
|
|
81
|
+
parsed_props[prop_name] = default_value;
|
|
82
|
+
} else {
|
|
83
|
+
delete parsed_props[prop_name];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { parsed_props, errors, raw_props };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async render(ctx: BaseContext) {
|
|
90
|
+
const { parsed_props, errors, raw_props } = await this.getProps(ctx);
|
|
91
|
+
|
|
92
|
+
return tempstream`${this.renderPagination(ctx, parsed_props)}
|
|
93
|
+
${this.renderFilters(ctx, parsed_props, raw_props, errors)}
|
|
94
|
+
${this.getItems(ctx, parsed_props).then(({ items }) =>
|
|
95
|
+
this.renderListContainer(
|
|
96
|
+
ctx,
|
|
97
|
+
items.map((item) => this.renderItem(ctx, item))
|
|
98
|
+
)
|
|
99
|
+
)}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async renderPagination(ctx: BaseContext, props: PropsType) {
|
|
103
|
+
const totalIems = await this.getTotalPages(ctx, props);
|
|
104
|
+
const currentPage = props.page;
|
|
105
|
+
|
|
106
|
+
return tempstream/* HTML */ `<center>
|
|
107
|
+
${currentPage > 1
|
|
108
|
+
? this.renderPageButton(ctx, 1, "Pierwsza strona")
|
|
109
|
+
: ""}
|
|
110
|
+
${currentPage > 1
|
|
111
|
+
? this.renderPageButton(
|
|
112
|
+
ctx,
|
|
113
|
+
currentPage - 1,
|
|
114
|
+
"Poprzednia strona"
|
|
115
|
+
)
|
|
116
|
+
: ""}
|
|
117
|
+
|
|
118
|
+
<select onchange="if (this.value) Turbo.visit(this.value)">
|
|
119
|
+
${Array.from(
|
|
120
|
+
naturalNumbers(1, await this.getTotalPages(ctx, props))
|
|
121
|
+
).map(
|
|
122
|
+
(n) => /* HTML */ `<option
|
|
123
|
+
value="${UrlWithNewParams(
|
|
124
|
+
ctx,
|
|
125
|
+
//eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
126
|
+
this.propsParser.overwriteProp(ctx, {
|
|
127
|
+
page: n,
|
|
128
|
+
} as Partial<PropsType>)
|
|
129
|
+
)}"
|
|
130
|
+
${currentPage === n ? "selected" : ""}
|
|
131
|
+
>
|
|
132
|
+
${n}
|
|
133
|
+
</option>`
|
|
134
|
+
)}
|
|
135
|
+
</select>
|
|
136
|
+
${currentPage < totalIems
|
|
137
|
+
? this.renderPageButton(ctx, currentPage + 1, "Następna strona")
|
|
138
|
+
: ""}
|
|
139
|
+
${currentPage < totalIems
|
|
140
|
+
? this.renderPageButton(ctx, totalIems, "Ostatnia strona")
|
|
141
|
+
: ""}
|
|
142
|
+
</center>`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private renderPageButton(ctx: BaseContext, page: number, text: string) {
|
|
146
|
+
return /* HTML */ `<a
|
|
147
|
+
href="${UrlWithNewParams(
|
|
148
|
+
ctx,
|
|
149
|
+
//eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
150
|
+
this.propsParser.overwriteProp(ctx, {
|
|
151
|
+
page,
|
|
152
|
+
} as Partial<PropsType>)
|
|
153
|
+
)}"
|
|
154
|
+
>${text}</a
|
|
155
|
+
>`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
renderFilters(
|
|
159
|
+
ctx: BaseContext,
|
|
160
|
+
parsed_props: PropsType, // parsed props don't include wrong values
|
|
161
|
+
raw_props: PropsType,
|
|
162
|
+
errors: PropsErrors<PropsType>
|
|
163
|
+
) {
|
|
164
|
+
return tempstream/* HTML */ `<form method="GET">
|
|
165
|
+
${this.propsParser.makeHiddenInputs(parsed_props, [
|
|
166
|
+
"page",
|
|
167
|
+
...this.filterFields.map((f) => f.name),
|
|
168
|
+
])}
|
|
169
|
+
${this.filterControls.map((control) =>
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
171
|
+
control.render(ctx, this.filterFields, {
|
|
172
|
+
values: parsed_props,
|
|
173
|
+
raw_values: raw_props,
|
|
174
|
+
errors,
|
|
175
|
+
messages: [],
|
|
176
|
+
} as FormData)
|
|
177
|
+
)}
|
|
178
|
+
</form>`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export abstract class SealiousItemListPage<
|
|
183
|
+
C extends Collection,
|
|
184
|
+
PageProps extends BaseListPageProps = BaseListPageProps
|
|
185
|
+
> extends ListPage<CollectionItem<C>, PageProps> {
|
|
186
|
+
constructor(public collection: C) {
|
|
187
|
+
super();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async getTotalPages(ctx: BaseContext, props: PageProps) {
|
|
191
|
+
const { items } = await this.collection.list(ctx.$context).fetch();
|
|
192
|
+
return Math.ceil(items.length / props.itemsPerPage);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async getItems(ctx: BaseContext, props: PageProps) {
|
|
196
|
+
return {
|
|
197
|
+
items: (
|
|
198
|
+
await this.collection
|
|
199
|
+
.list(ctx.$context)
|
|
200
|
+
.paginate({ items: props.itemsPerPage, page: props.page })
|
|
201
|
+
.fetch()
|
|
202
|
+
).items,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async renderItem(
|
|
207
|
+
_: BaseContext,
|
|
208
|
+
item: CollectionItem<C>
|
|
209
|
+
): Promise<Templatable> {
|
|
210
|
+
return `<div>${item.id}</div>`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
canAccess = peopleWhoCan("list", this.collection);
|
|
214
|
+
}
|
package/src/page/page.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Router from "@koa/router";
|
|
2
|
+
import { BaseContext } from "koa";
|
|
3
|
+
|
|
4
|
+
import { Templatable, tempstream } from "tempstream";
|
|
5
|
+
|
|
6
|
+
export type PageErrorMessage = { type: "access" | "internal"; message: string };
|
|
7
|
+
|
|
8
|
+
export interface Mountable {
|
|
9
|
+
mount: (router: Router, path: string) => void;
|
|
10
|
+
canAccess: (ctx: BaseContext) => Promise<{ canAccess: boolean; message: string }>;
|
|
11
|
+
renderError(ctx: BaseContext, error: PageErrorMessage): Promise<Templatable>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export abstract class Page implements Mountable {
|
|
15
|
+
mount(router: Router, path: string) {
|
|
16
|
+
router.get(path, async (ctx) => {
|
|
17
|
+
ctx.body = await this.render(ctx);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
22
|
+
async canAccess(_: BaseContext) {
|
|
23
|
+
return { canAccess: true, message: "" };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async renderError(_: BaseContext, error: PageErrorMessage) {
|
|
27
|
+
return tempstream/* HTML */ `<div>${error.message}</div>`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public abstract render(ctx: BaseContext): Promise<Templatable>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hasShape,
|
|
3
|
+
predicates,
|
|
4
|
+
Shape,
|
|
5
|
+
ShapeToType,
|
|
6
|
+
} from "@sealcode/ts-predicates";
|
|
7
|
+
|
|
8
|
+
import { BaseContext } from "koa";
|
|
9
|
+
import merge from "merge";
|
|
10
|
+
import { BasePageProps } from "./list";
|
|
11
|
+
|
|
12
|
+
export type EncodedProps = Record<string, unknown>;
|
|
13
|
+
|
|
14
|
+
// the intention here is to sometime in the future be able to store multiple frames on one document, so props for each frame will be in a different namespace, and parsers are going to help with that
|
|
15
|
+
|
|
16
|
+
function parseStringValues<TheShape extends Shape>(
|
|
17
|
+
shape: TheShape,
|
|
18
|
+
values: Record<string, string>
|
|
19
|
+
): ShapeToType<TheShape> {
|
|
20
|
+
const result: Record<string, unknown> = {};
|
|
21
|
+
for (const [key, value] of Object.entries(values)) {
|
|
22
|
+
if (!(key in shape)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const predicate = shape[key];
|
|
26
|
+
if (predicate == predicates.number) {
|
|
27
|
+
result[key] = parseFloat(value);
|
|
28
|
+
} else {
|
|
29
|
+
result[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
33
|
+
return result as ShapeToType<TheShape>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export abstract class PagePropsParser<PropsType extends BasePageProps> {
|
|
37
|
+
abstract decode(ctx: BaseContext): PropsType;
|
|
38
|
+
abstract encode(props: PropsType): EncodedProps;
|
|
39
|
+
abstract getHTMLInputName(prop_name: string): string;
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
public propsShape: Shape,
|
|
43
|
+
public defaultValues: Partial<PropsType>
|
|
44
|
+
) {}
|
|
45
|
+
|
|
46
|
+
overwriteProp(
|
|
47
|
+
ctx: BaseContext,
|
|
48
|
+
new_props: Partial<PropsType>
|
|
49
|
+
): EncodedProps {
|
|
50
|
+
const result = {};
|
|
51
|
+
merge.recursive(result, this.decode(ctx), new_props);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
makeHiddenInputs(values: PropsType, fields_to_skip: string[]): string {
|
|
56
|
+
return Object.entries(values)
|
|
57
|
+
.filter(([key]) => !fields_to_skip.includes(key))
|
|
58
|
+
.map(
|
|
59
|
+
([key, value]: [string, string | number]) =>
|
|
60
|
+
/* HTML */ `<input
|
|
61
|
+
type="hidden"
|
|
62
|
+
name="${key}"
|
|
63
|
+
value="${value}"
|
|
64
|
+
/>`
|
|
65
|
+
)
|
|
66
|
+
.join(" ");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getDefaultValue<Key extends keyof PropsType>(
|
|
70
|
+
key: Key
|
|
71
|
+
): PropsType[Key] | undefined {
|
|
72
|
+
return this.defaultValues[key];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class AllQueryParams<
|
|
77
|
+
PropsType extends BasePageProps
|
|
78
|
+
> extends PagePropsParser<PropsType> {
|
|
79
|
+
decode(ctx: BaseContext): PropsType {
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
81
|
+
const query = parseStringValues(this.propsShape, {
|
|
82
|
+
...this.defaultValues,
|
|
83
|
+
...ctx.query,
|
|
84
|
+
} as unknown as Record<string, string>);
|
|
85
|
+
if (!hasShape(this.propsShape, query)) {
|
|
86
|
+
throw new Error("Wrong props shape");
|
|
87
|
+
}
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
89
|
+
return query as PropsType;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
encode(props: PropsType): Record<string, unknown> {
|
|
93
|
+
return props;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getHTMLInputName(prop_name: string): string {
|
|
97
|
+
return prop_name;
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/run.ts
ADDED
|
File without changes
|
package/src/templates/form.ts
CHANGED
|
@@ -6,11 +6,16 @@ export async function formTemplate(
|
|
|
6
6
|
): Promise<string> {
|
|
7
7
|
const rel = curryImportPath(newfilefullpath);
|
|
8
8
|
return `import { BaseContext } from "koa";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
import {
|
|
10
|
+
Form,
|
|
11
|
+
FormData,
|
|
12
|
+
FormHeader,
|
|
13
|
+
SimpleInput,
|
|
14
|
+
FormField,
|
|
15
|
+
collectionFieldValidator,
|
|
16
|
+
} from "@sealcode/sealgen";
|
|
17
|
+
|
|
18
|
+
import { Users } from "${rel("src/back/collections/collections")}";
|
|
14
19
|
import html from "${rel("src/back/html")}";
|
|
15
20
|
|
|
16
21
|
export const actionName = "${action_name}";
|
package/src/templates/list.ts
CHANGED
|
@@ -22,7 +22,7 @@ export const listTemplate = async (
|
|
|
22
22
|
collection[0].toLocaleUpperCase() + collection.slice(1);
|
|
23
23
|
const rel = curryImportPath(newfilefullpath);
|
|
24
24
|
return `import { BaseContext } from "koa";
|
|
25
|
-
import { CollectionItem
|
|
25
|
+
import { CollectionItem } from "sealious";
|
|
26
26
|
import { tempstream } from "tempstream";
|
|
27
27
|
import { ${uppercase_collection} } from "${rel(
|
|
28
28
|
"src/back/collections/collections"
|
|
@@ -33,8 +33,8 @@ import {
|
|
|
33
33
|
BaseListPageProps,
|
|
34
34
|
BaseListPagePropsShape,
|
|
35
35
|
SealiousItemListPage,
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
AllQueryParams,
|
|
37
|
+
} from "@sealcode/sealgen";
|
|
38
38
|
|
|
39
39
|
export const actionName = "${action_name}";
|
|
40
40
|
|
package/src/templates/page.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { curryImportPath } from "../utils/import-path";
|
|
3
2
|
export async function pageTemplate(
|
|
4
3
|
action_name: string,
|
|
5
|
-
|
|
4
|
+
newfilefullpath: string
|
|
6
5
|
): Promise<string> {
|
|
6
|
+
const rel = curryImportPath(newfilefullpath);
|
|
7
7
|
return `import { BaseContext } from "koa";
|
|
8
8
|
import { tempstream } from "tempstream";
|
|
9
|
-
import { Page } from "
|
|
9
|
+
import { Page } from "@sealcode/sealgen";
|
|
10
|
+
import html from "${rel("src/back/html")}";
|
|
10
11
|
|
|
11
12
|
export const actionName = "${action_name}";
|
|
12
13
|
|
|
13
14
|
export default new (class ${action_name}Page extends Page {
|
|
14
|
-
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
16
16
|
async canAccess(_: BaseContext) {
|
|
17
17
|
return { canAccess: true, message: "" };
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async render(ctx: BaseContext) {
|
|
21
|
-
return tempstream/* HTML */ \`<div></div
|
|
21
|
+
return html(ctx, "${action_name}", tempstream/* HTML */ \`<div></div>\`);
|
|
22
22
|
}
|
|
23
23
|
})();
|
|
24
24
|
`;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import _locreq from "locreq";
|
|
2
|
-
import {
|
|
2
|
+
import { walkDir } from "./walk";
|
|
3
3
|
const target_locreq = _locreq(process.cwd());
|
|
4
4
|
|
|
5
5
|
export async function listCollections(): Promise<string[]> {
|
|
6
|
-
const files = await
|
|
6
|
+
const files = await walkDir(target_locreq.resolve("src/back/collections"));
|
|
7
7
|
return files
|
|
8
8
|
.filter(
|
|
9
9
|
(f) =>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type Awaited<T> = T extends Promise<infer U> ? U : T;
|
|
2
|
+
export type UnwrapArray<T> = T extends Array<infer U> ? U : T;
|
|
3
|
+
|
|
4
|
+
export { sleep } from "./sleep";
|
|
5
|
+
export { naturalNumbers } from "./natural-numbers";
|
|
6
|
+
export { UrlWithNewParams } from "./url-with-new-params";
|
|
7
|
+
export { exec } from "./exec";
|
|
8
|
+
export { importPath, curryImportPath } from "./import-path";
|
|
9
|
+
export { listCollections } from "./list-collections";
|
|
10
|
+
export { walkDir } from "./walk";
|
package/src/utils/walk.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from "fs";
|
|
2
2
|
import { resolve } from "path";
|
|
3
3
|
|
|
4
|
-
export async function
|
|
4
|
+
export async function walkDir(dir: string): Promise<string[]> {
|
|
5
5
|
const entries = await fs.readdir(dir);
|
|
6
6
|
let ret: string[] = [];
|
|
7
7
|
for (const entry of entries) {
|
|
@@ -11,7 +11,7 @@ export async function walk(dir: string): Promise<string[]> {
|
|
|
11
11
|
|
|
12
12
|
if (info.isDirectory()) {
|
|
13
13
|
// eslint-disable-next-line no-await-in-loop
|
|
14
|
-
ret = [...ret, ...(await
|
|
14
|
+
ret = [...ret, ...(await walkDir(fullpath))];
|
|
15
15
|
} else {
|
|
16
16
|
ret = [...ret, fullpath];
|
|
17
17
|
}
|
package/tsconfig.json
CHANGED
|
@@ -14,8 +14,11 @@
|
|
|
14
14
|
"allowJs": true,
|
|
15
15
|
"declarationDir": "@types",
|
|
16
16
|
"resolveJsonModule": true,
|
|
17
|
-
"sourceMap": true
|
|
17
|
+
"sourceMap": true,
|
|
18
|
+
"allowSyntheticDefaultImports": true,
|
|
19
|
+
"keyofStringsOnly": true,
|
|
20
|
+
"skipLibCheck": true
|
|
18
21
|
},
|
|
19
22
|
"include": ["src/**/*"],
|
|
20
|
-
"exclude": ["src/**/*.test.ts"]
|
|
23
|
+
"exclude": ["src/**/*.test.ts", "node_modules/**"]
|
|
21
24
|
}
|