@sealcode/sealgen 0.1.7 → 0.2.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.
Files changed (165) hide show
  1. package/.eslintrc.js +1 -0
  2. package/.log/ti-101379.log +524 -0
  3. package/.log/ti-131883.log +505 -0
  4. package/.log/ti-138140.log +505 -0
  5. package/.log/ti-138288.log +526 -0
  6. package/.log/ti-292609.log +508 -0
  7. package/.log/ti-304933.log +524 -0
  8. package/.log/ti-312312.log +508 -0
  9. package/.log/ti-314240.log +1617 -0
  10. package/.log/ti-62627.log +508 -0
  11. package/.log/ti-64785.log +508 -0
  12. package/.log/ti-65911.log +533 -0
  13. package/.log/ti-69885.log +548 -0
  14. package/.log/ti-72106.log +551 -0
  15. package/.log/ti-74858.log +508 -0
  16. package/.log/tsserver.log +1610 -902
  17. package/@types/build.d.ts +1 -0
  18. package/@types/cli.d.ts +2 -0
  19. package/@types/forms/controls/checkboxed-list-input.d.ts +16 -0
  20. package/@types/forms/controls/controls.d.ts +9 -0
  21. package/@types/forms/controls/dropdown.d.ts +20 -0
  22. package/@types/forms/controls/editable-collection-subset.d.ts +21 -0
  23. package/@types/forms/controls/form-control.d.ts +8 -0
  24. package/@types/forms/controls/form-field-control.d.ts +13 -0
  25. package/@types/forms/controls/form-header.d.ts +9 -0
  26. package/@types/forms/controls/form-paragraph.d.ts +7 -0
  27. package/@types/forms/controls/frame.d.ts +17 -0
  28. package/@types/forms/controls/simple-input.d.ts +28 -0
  29. package/@types/forms/controls.d.ts +0 -0
  30. package/@types/forms/field.d.ts +42 -0
  31. package/@types/forms/form-fields-list.d.ts +4 -0
  32. package/@types/forms/form.d.ts +42 -0
  33. package/@types/forms/validator.d.ts +3 -0
  34. package/@types/index.d.ts +14 -2
  35. package/@types/page/access-control.d.ts +6 -0
  36. package/@types/page/list.d.ts +59 -0
  37. package/@types/page/page.d.ts +26 -0
  38. package/@types/page/props-parser.d.ts +20 -0
  39. package/@types/run.d.ts +0 -0
  40. package/@types/templates/form.d.ts +1 -1
  41. package/@types/templates/list.d.ts +1 -0
  42. package/@types/templates/page.d.ts +1 -1
  43. package/@types/utils/list-collections.d.ts +1 -0
  44. package/@types/utils/natural-numbers.d.ts +1 -0
  45. package/@types/utils/sleep.d.ts +1 -0
  46. package/@types/utils/url-with-new-params.d.ts +2 -0
  47. package/@types/utils/utils.d.ts +9 -0
  48. package/@types/utils/walk.d.ts +1 -1
  49. package/lib/add-collection.js +13 -24
  50. package/lib/add-collection.js.map +1 -1
  51. package/lib/add-route.js +25 -32
  52. package/lib/add-route.js.map +1 -1
  53. package/lib/build.js +108 -0
  54. package/lib/build.js.map +1 -0
  55. package/lib/cli.js +30 -0
  56. package/lib/cli.js.map +1 -0
  57. package/lib/find-scss-includes.js +10 -20
  58. package/lib/find-scss-includes.js.map +1 -1
  59. package/lib/forms/controls/checkboxed-list-input.js +43 -0
  60. package/lib/forms/controls/checkboxed-list-input.js.map +1 -0
  61. package/lib/forms/controls/controls.js +22 -0
  62. package/lib/forms/controls/controls.js.map +1 -0
  63. package/lib/forms/controls/dropdown.js +40 -0
  64. package/lib/forms/controls/dropdown.js.map +1 -0
  65. package/lib/forms/controls/editable-collection-subset.js +73 -0
  66. package/lib/forms/controls/editable-collection-subset.js.map +1 -0
  67. package/lib/forms/controls/form-control.js +7 -0
  68. package/lib/forms/controls/form-control.js.map +1 -0
  69. package/lib/forms/controls/form-field-control.js +22 -0
  70. package/lib/forms/controls/form-field-control.js.map +1 -0
  71. package/lib/forms/controls/form-header.js +18 -0
  72. package/lib/forms/controls/form-header.js.map +1 -0
  73. package/lib/forms/controls/form-paragraph.js +16 -0
  74. package/lib/forms/controls/form-paragraph.js.map +1 -0
  75. package/lib/forms/controls/frame.js +29 -0
  76. package/lib/forms/controls/frame.js.map +1 -0
  77. package/lib/forms/controls/simple-input.js +50 -0
  78. package/lib/forms/controls/simple-input.js.map +1 -0
  79. package/lib/forms/controls.js +1 -0
  80. package/lib/forms/controls.js.map +1 -0
  81. package/lib/forms/field.js +80 -0
  82. package/lib/forms/field.js.map +1 -0
  83. package/lib/forms/form-fields-list.js +10 -0
  84. package/lib/forms/form-fields-list.js.map +1 -0
  85. package/lib/forms/form.js +162 -0
  86. package/lib/forms/form.js.map +1 -0
  87. package/lib/forms/validator.js +11 -0
  88. package/lib/forms/validator.js.map +1 -0
  89. package/lib/generate-collections.js +30 -43
  90. package/lib/generate-collections.js.map +1 -1
  91. package/lib/generate-routes.js +29 -42
  92. package/lib/generate-routes.js.map +1 -1
  93. package/lib/index.js +16 -37
  94. package/lib/index.js.map +1 -1
  95. package/lib/page/access-control.js +20 -0
  96. package/lib/page/access-control.js.map +1 -0
  97. package/lib/page/list.js +148 -0
  98. package/lib/page/list.js.map +1 -0
  99. package/lib/page/page.js +20 -0
  100. package/lib/page/page.js.map +1 -0
  101. package/lib/page/props-parser.js +74 -0
  102. package/lib/page/props-parser.js.map +1 -0
  103. package/lib/run.js +1 -0
  104. package/lib/run.js.map +1 -0
  105. package/lib/templates/form.js +2 -2
  106. package/lib/templates/form.js.map +1 -1
  107. package/lib/templates/list.js +61 -0
  108. package/lib/templates/list.js.map +1 -0
  109. package/lib/templates/page.js +3 -1
  110. package/lib/templates/page.js.map +1 -1
  111. package/lib/utils/exec.js +16 -27
  112. package/lib/utils/exec.js.map +1 -1
  113. package/lib/utils/list-collections.js +20 -0
  114. package/lib/utils/list-collections.js.map +1 -0
  115. package/lib/utils/natural-numbers.js +10 -0
  116. package/lib/utils/natural-numbers.js.map +1 -0
  117. package/lib/utils/question.js +15 -26
  118. package/lib/utils/question.js.map +1 -1
  119. package/lib/utils/sleep.js +8 -0
  120. package/lib/utils/sleep.js.map +1 -0
  121. package/lib/utils/url-with-new-params.js +12 -0
  122. package/lib/utils/url-with-new-params.js.map +1 -0
  123. package/lib/utils/utils.js +19 -0
  124. package/lib/utils/utils.js.map +1 -0
  125. package/lib/utils/walk.js +16 -27
  126. package/lib/utils/walk.js.map +1 -1
  127. package/package.json +19 -4
  128. package/src/add-route.ts +14 -9
  129. package/src/build.ts +82 -0
  130. package/src/cli.ts +34 -0
  131. package/src/find-scss-includes.ts +3 -2
  132. package/src/forms/controls/checkboxed-list-input.ts +50 -0
  133. package/src/forms/controls/controls.ts +9 -0
  134. package/src/forms/controls/dropdown.ts +57 -0
  135. package/src/forms/controls/editable-collection-subset.ts +91 -0
  136. package/src/forms/controls/form-control.ts +13 -0
  137. package/src/forms/controls/form-field-control.ts +39 -0
  138. package/src/forms/controls/form-header.ts +16 -0
  139. package/src/forms/controls/form-paragraph.ts +11 -0
  140. package/src/forms/controls/frame.ts +26 -0
  141. package/src/forms/controls/simple-input.ts +73 -0
  142. package/src/forms/controls.ts +0 -0
  143. package/src/forms/field.ts +119 -0
  144. package/src/forms/form-fields-list.ts +7 -0
  145. package/src/forms/form.ts +242 -0
  146. package/src/forms/forms.scss +18 -0
  147. package/src/forms/validator.ts +10 -0
  148. package/src/generate-routes.ts +8 -3
  149. package/src/index.ts +14 -28
  150. package/src/page/access-control.ts +18 -0
  151. package/src/page/list.ts +214 -0
  152. package/src/page/page.ts +31 -0
  153. package/src/page/props-parser.ts +99 -0
  154. package/src/run.ts +0 -0
  155. package/src/templates/form.ts +5 -2
  156. package/src/templates/list.ts +65 -0
  157. package/src/templates/page.ts +4 -2
  158. package/src/utils/list-collections.ts +16 -0
  159. package/src/utils/natural-numbers.ts +5 -0
  160. package/src/utils/sleep.ts +3 -0
  161. package/src/utils/url-with-new-params.ts +9 -0
  162. package/src/utils/utils.ts +10 -0
  163. package/src/utils/walk.ts +2 -2
  164. package/tsconfig.json +23 -25
  165. package/src/tsconfig.json +0 -18
@@ -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
+ }
@@ -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
@@ -1,6 +1,9 @@
1
1
  import { curryImportPath } from "../utils/import-path";
2
2
 
3
- export const formTemplate = (action_name: string, newfilefullpath: string) => {
3
+ export async function formTemplate(
4
+ action_name: string,
5
+ newfilefullpath: string
6
+ ): Promise<string> {
4
7
  const rel = curryImportPath(newfilefullpath);
5
8
  return `import { BaseContext } from "koa";
6
9
  import {Users} from "${rel("src/back/collections/collections")}";
@@ -39,4 +42,4 @@ export default new(class ${action_name}Form extends Form {
39
42
  }
40
43
  })();
41
44
  `;
42
- };
45
+ }
@@ -0,0 +1,65 @@
1
+ import { curryImportPath } from "../utils/import-path";
2
+ import prompts = require("prompts");
3
+ import { listCollections } from "../utils/list-collections";
4
+
5
+ export const listTemplate = async (
6
+ action_name: string,
7
+ newfilefullpath: string
8
+ ): Promise<string> => {
9
+ const response = await prompts({
10
+ type: "autocomplete",
11
+ name: "collection",
12
+ message: "Which sealious collection do you want to list?",
13
+ choices: (
14
+ await listCollections()
15
+ ).map((collection) => ({
16
+ title: collection,
17
+ value: collection,
18
+ })),
19
+ });
20
+ const collection = response.collection as string;
21
+ const uppercase_collection =
22
+ collection[0].toLocaleUpperCase() + collection.slice(1);
23
+ const rel = curryImportPath(newfilefullpath);
24
+ return `import { BaseContext } from "koa";
25
+ import { CollectionItem } from "sealious";
26
+ import { tempstream } from "tempstream";
27
+ import { ${uppercase_collection} } from "${rel(
28
+ "src/back/collections/collections"
29
+ )}";
30
+ import html from "${rel("src/back/html.ts")}";
31
+ import {
32
+ BaseListPageDefaultProps,
33
+ BaseListPageProps,
34
+ BaseListPagePropsShape,
35
+ SealiousItemListPage,
36
+ } from "${rel("src/back/page/list.ts")}";
37
+ import { AllQueryParams } from "${rel("src/back/page/props-parser.ts")}";
38
+
39
+ export const actionName = "${action_name}";
40
+
41
+ export default new (class ${action_name}Page extends SealiousItemListPage<typeof ${uppercase_collection}> {
42
+ propsParser = new AllQueryParams<BaseListPageProps>(
43
+ BaseListPagePropsShape,
44
+ BaseListPageDefaultProps
45
+ );
46
+
47
+ async render(ctx: BaseContext) {
48
+ return html(
49
+ ctx,
50
+ "${uppercase_collection}",
51
+ tempstream/* HTML */ \`<div>
52
+ <h2>${uppercase_collection} List</h2>
53
+ <ul>
54
+ \${super.render(ctx)}
55
+ </ul>
56
+ </div>\`
57
+ );
58
+ }
59
+
60
+ async renderItem(_: BaseContext, item: CollectionItem<typeof ${uppercase_collection}>) {
61
+ return tempstream\`<li>\${item.id}</li>\`;
62
+ }
63
+ })(${uppercase_collection});
64
+ `;
65
+ };
@@ -1,9 +1,10 @@
1
1
  import { importPath } from "../utils/import-path";
2
2
 
3
- export const pageTemplate = (
3
+ export async function pageTemplate(
4
4
  action_name: string,
5
5
  fullpath: string
6
- ) => `import { BaseContext } from "koa";
6
+ ): Promise<string> {
7
+ return `import { BaseContext } from "koa";
7
8
  import { tempstream } from "tempstream";
8
9
  import { Page } from "${importPath(fullpath, "src/back/page/page")}";
9
10
 
@@ -21,3 +22,4 @@ export default new (class ${action_name}Page extends Page {
21
22
  }
22
23
  })();
23
24
  `;
25
+ }
@@ -0,0 +1,16 @@
1
+ import _locreq from "locreq";
2
+ import { walkDir } from "./walk";
3
+ const target_locreq = _locreq(process.cwd());
4
+
5
+ export async function listCollections(): Promise<string[]> {
6
+ const files = await walkDir(target_locreq.resolve("src/back/collections"));
7
+ return files
8
+ .filter(
9
+ (f) =>
10
+ f.endsWith(".ts") &&
11
+ !f.endsWith(".test.ts") &&
12
+ !f.endsWith("collections.ts")
13
+ )
14
+ .map((f) => f.replace(/.ts$/, "").split("/").at(-1) || "")
15
+ .filter((e) => e !== "");
16
+ }
@@ -0,0 +1,5 @@
1
+ export function* naturalNumbers(min: number, max: number) {
2
+ for (let i = min; i <= max; i++) {
3
+ yield i;
4
+ }
5
+ }
@@ -0,0 +1,3 @@
1
+ export async function sleep(time: number) {
2
+ return new Promise((resolve) => setTimeout(resolve, time));
3
+ }
@@ -0,0 +1,9 @@
1
+ import { BaseContext } from "koa";
2
+ import qs from "qs";
3
+
4
+ export function UrlWithNewParams(
5
+ ctx: BaseContext,
6
+ query_params: Record<string, unknown>
7
+ ): string {
8
+ return `${ctx.path}?${qs.stringify(query_params)}`;
9
+ }
@@ -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 walk(dir: string): Promise<string[]> {
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 walk(fullpath))];
14
+ ret = [...ret, ...(await walkDir(fullpath))];
15
15
  } else {
16
16
  ret = [...ret, fullpath];
17
17
  }
package/tsconfig.json CHANGED
@@ -1,26 +1,24 @@
1
1
  {
2
- "compilerOptions": {
3
- "module": "commonjs",
4
- "moduleResolution": "node",
5
- "noImplicitAny": true,
6
- "noImplicitThis": true,
7
- "strictNullChecks": true,
8
- "target": "ES6",
9
- "declaration": true,
10
- "esModuleInterop": true,
11
- "lib": [
12
- "ES6",
13
- "ESNext"
14
- ],
15
- "outDir": "lib",
16
- "checkJs": true,
17
- "allowJs": true,
18
- "declarationDir": "@types",
19
- "resolveJsonModule": true,
20
- "sourceMap": true
21
- },
22
- "include":[
23
- "src/**/*"
24
- ],
25
- "exclude": ["src/**/*.test.ts"]
26
- }
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "moduleResolution": "node",
5
+ "noImplicitAny": true,
6
+ "noImplicitThis": true,
7
+ "strictNullChecks": true,
8
+ "target": "es2020",
9
+ "declaration": true,
10
+ "esModuleInterop": true,
11
+ "lib": ["ES6", "ESNext"],
12
+ "outDir": "lib",
13
+ "checkJs": true,
14
+ "allowJs": true,
15
+ "declarationDir": "@types",
16
+ "resolveJsonModule": true,
17
+ "sourceMap": true,
18
+ "allowSyntheticDefaultImports": true,
19
+ "keyofStringsOnly": true,
20
+ "skipLibCheck": true
21
+ },
22
+ "include": ["src/**/*"],
23
+ "exclude": ["src/**/*.test.ts", "node_modules/**"]
24
+ }
package/src/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "ESNext",
4
- "moduleResolution": "node",
5
- "noImplicitAny": true,
6
- "noImplicitThis": true,
7
- "strictNullChecks": true,
8
- "target": "ES2020",
9
- "lib": ["es6", "esnext"],
10
- "outDir": "../../dist/scripts",
11
- "checkJs": true,
12
- "allowJs": true,
13
- "resolveJsonModule": true,
14
- "sourceMap": true,
15
- "skipLibCheck": true
16
- },
17
- "include": ["./**/*", "./*"]
18
- }