@sealcode/sealgen 0.15.35 → 0.15.37

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 (85) hide show
  1. package/.xunit +59 -57
  2. package/@types/src/forms/controls/form-control.d.ts +2 -1
  3. package/@types/src/forms/controls/table.d.ts +24 -5
  4. package/@types/src/forms/fields/table.d.ts +4 -2
  5. package/@types/src/templates/form.d.ts +5 -0
  6. package/@types/src/templates/shared/collection-create-form.d.ts +0 -2
  7. package/@types/src/templates/shared/handlers/boolean.d.ts +2 -0
  8. package/@types/src/templates/shared/handlers/date.d.ts +2 -0
  9. package/@types/src/templates/shared/handlers/deep-reverse-single-reference.d.ts +2 -0
  10. package/@types/src/templates/shared/handlers/default.d.ts +2 -0
  11. package/@types/src/templates/shared/handlers/enum.d.ts +2 -0
  12. package/@types/src/templates/shared/handlers/file.d.ts +2 -0
  13. package/@types/src/templates/shared/handlers/image.d.ts +2 -0
  14. package/@types/src/templates/shared/handlers/int.d.ts +2 -0
  15. package/@types/src/templates/shared/handlers/jdd.d.ts +2 -0
  16. package/@types/src/templates/shared/handlers/single-reference.d.ts +2 -0
  17. package/@types/src/templates/shared/handlers/structured-array.d.ts +2 -0
  18. package/@types/src/templates/shared/shared-crud-form-fields.d.ts +29 -6
  19. package/@types/src/utils/extract-fields-from-collection.d.ts +12 -2
  20. package/@types/src/utils/with-fallback.d.ts +1 -1
  21. package/lib/add-crud.js +5 -5
  22. package/lib/cli.js +2 -0
  23. package/lib/forms/controls/form-control.js +2 -2
  24. package/lib/forms/controls/multiple-photos.js +2 -1
  25. package/lib/forms/controls/single-reference.test.js +1 -1
  26. package/lib/forms/controls/table.js +114 -12
  27. package/lib/forms/fields/boolean.js +1 -1
  28. package/lib/forms/fields/field.js +4 -1
  29. package/lib/forms/fields/table.js +8 -3
  30. package/lib/forms/form.js +7 -6
  31. package/lib/page/default-list-filters.js +1 -0
  32. package/lib/templates/form.js +25 -16
  33. package/lib/templates/list.js +6 -167
  34. package/lib/templates/shared/collection-create-form.js +23 -83
  35. package/lib/templates/shared/collection-edit-form.js +53 -96
  36. package/lib/templates/shared/collection-list.js +21 -6
  37. package/lib/templates/shared/confirm-item-delete.js +7 -5
  38. package/lib/templates/shared/handlers/boolean.js +14 -0
  39. package/lib/templates/shared/handlers/date.js +8 -0
  40. package/lib/templates/shared/handlers/deep-reverse-single-reference.js +209 -0
  41. package/lib/templates/shared/handlers/default.js +50 -0
  42. package/lib/templates/shared/handlers/enum.js +19 -0
  43. package/lib/templates/shared/handlers/file.js +18 -0
  44. package/lib/templates/shared/handlers/image.js +32 -0
  45. package/lib/templates/shared/handlers/int.js +11 -0
  46. package/lib/templates/shared/handlers/jdd.js +10 -0
  47. package/lib/templates/shared/handlers/single-reference.js +12 -0
  48. package/lib/templates/shared/handlers/structured-array.js +33 -0
  49. package/lib/templates/shared/item-delete.js +5 -3
  50. package/lib/templates/shared/shared-crud-form-fields.js +72 -82
  51. package/lib/utils/extract-fields-from-collection.js +31 -5
  52. package/package.json +2 -1
  53. package/src/add-crud.ts +153 -0
  54. package/src/cli.ts +2 -0
  55. package/src/forms/controls/form-control.ts +3 -2
  56. package/src/forms/controls/multiple-photos.ts +1 -0
  57. package/src/forms/controls/single-reference.test.ts +1 -1
  58. package/src/forms/controls/table.ts +168 -31
  59. package/src/forms/fields/boolean.ts +1 -1
  60. package/src/forms/fields/field.ts +4 -1
  61. package/src/forms/fields/table.ts +25 -6
  62. package/src/forms/form.ts +13 -7
  63. package/src/page/default-list-filters.ts +1 -0
  64. package/src/templates/form.ts +24 -15
  65. package/src/templates/list.ts +6 -174
  66. package/src/templates/shared/collection-create-form.ts +121 -0
  67. package/src/templates/shared/collection-edit-form.ts +196 -0
  68. package/src/templates/shared/collection-list.ts +252 -0
  69. package/src/templates/shared/confirm-item-delete.ts +78 -0
  70. package/src/templates/shared/handlers/boolean.ts +18 -0
  71. package/src/templates/shared/handlers/date.ts +12 -0
  72. package/src/templates/shared/handlers/deep-reverse-single-reference.ts +244 -0
  73. package/src/templates/shared/handlers/default.ts +58 -0
  74. package/src/templates/shared/handlers/enum.ts +23 -0
  75. package/src/templates/shared/handlers/file.ts +22 -0
  76. package/src/templates/shared/handlers/image.ts +45 -0
  77. package/src/templates/shared/handlers/int.ts +15 -0
  78. package/src/templates/shared/handlers/jdd.ts +14 -0
  79. package/src/templates/shared/handlers/single-reference.ts +16 -0
  80. package/src/templates/shared/handlers/structured-array.ts +42 -0
  81. package/src/templates/shared/item-delete.ts +45 -0
  82. package/src/templates/shared/shared-crud-form-fields.ts +179 -0
  83. package/src/utils/extract-fields-from-collection.ts +45 -6
  84. package/src/utils/with-fallback.ts +1 -1
  85. package/src/utils/write-file.ts +8 -0
@@ -1,109 +1,37 @@
1
- import { toCamelCase, toKebabCase, toPascalCase } from "js-convert-case";
1
+ import { toKebabCase, toPascalCase } from "js-convert-case";
2
2
  import { formTemplate } from "../form.js";
3
3
  import { curryImportPath } from "../../utils/import-path.js";
4
4
  import extract_fields_from_collection from "../../utils/extract-fields-from-collection.js";
5
- import { renderFieldForSealious } from "./collection-create-form.js";
6
- function renderFieldForInitialValues({
7
- name,
8
- type,
9
- is_required,
10
- is_readonly
11
- }) {
12
- if (type == "deep-reverse-single-reference") {
13
- return `${name}: Object.fromEntries((item.get("${name}") || []).map(id=>[id, "on"]))`;
14
- }
15
- if (type == "jdd" || is_readonly) {
16
- return "";
17
- }
18
- if (type == "image" || type == "file") {
19
- return `${name}: {old: item.get("${name}")}`;
20
- }
21
- let fallback_value = `""`;
22
- let main_value = `item.get("${name}")`;
23
- if (type == "boolean") {
24
- fallback_value = `"false"`;
25
- main_value = `String(item.get("${name}"))`;
26
- }
27
- if (is_required) {
28
- return `${name}: ${main_value}`;
29
- } else {
30
- return `${name}: ${main_value} || ${fallback_value}`;
31
- }
32
- }
33
- function renderFieldPreEditAction({ name, type }) {
34
- if (type == "image" || type == "file") {
35
- const tempname = toCamelCase("prepared-" + name);
36
- return `const ${tempname} = data.${name}.new || data.${name}.old;
37
- if(!${tempname}){
38
- throw new Error("Missing field: ${name}")
39
- }`;
40
- }
41
- return "";
42
- }
43
- function renderFieldPostEditAction({
44
- name,
45
- type,
46
- referencing_collection,
47
- referencing_field,
48
- intermediary_field_that_points_there
49
- }) {
50
- if (type == "deep-reverse-single-reference" && referencing_collection && referencing_field && intermediary_field_that_points_there) {
51
- return `{
52
- const item_id = await this.getID(ctx);
53
- // First, we remove links refering to current item
54
- const {items:to_delete} = (await ctx.$app.collections["${referencing_collection}"]
55
- .list(ctx.$context)
56
- .filter({"${referencing_field}": item_id})
57
- .fetch()
58
- )
59
- await Promise.all(
60
- to_delete.map((item) => item.remove(ctx.$context))
61
- );
62
-
63
- // Then, we iterate over all selected values and insert appropriate entries for the ${name} relation
64
- const ids = Object.entries(
65
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
66
- data.${name} as unknown as Record<string, string>
67
- )
68
- .filter(([, value]) => value === "on")
69
- .map(([key]) => key);
70
-
71
- const promises = ids.map(async (id) => {
72
- await ctx.$app.collections["${referencing_collection}"].create(
73
- new ctx.$app.SuperContext(),
74
- {
75
- "${referencing_field}": item_id,
76
- "${intermediary_field_that_points_there}": id,
77
- }
78
- );
79
- });
80
-
81
- await Promise.all(promises);
82
- }`;
83
- }
84
- return "";
85
- }
5
+ import { getFieldHandler } from "./shared-crud-form-fields.js";
86
6
  async function editItemFormTemplate(edit_action_name, newfilefullpath, collection_name, list_action) {
87
7
  const collection_fields = await extract_fields_from_collection(
88
8
  collection_name
89
9
  );
90
10
  const importPath = curryImportPath(newfilefullpath);
11
+ const field_handler_results = await Promise.all(
12
+ collection_fields.map(async (field) => {
13
+ return getFieldHandler(collection_name, field, importPath);
14
+ })
15
+ );
91
16
  return formTemplate(edit_action_name, newfilefullpath, {
92
17
  postimport: `import {${toPascalCase(
93
18
  collection_name
94
19
  )}FormFields, ${toPascalCase(
95
20
  collection_name
96
21
  )}FormControls }from "../shared.js";
97
-
98
- import { ${toPascalCase(collection_name)} } from "${importPath(
22
+ import { ${toPascalCase(collection_name)} } from "${importPath(
99
23
  "src/back/collections/collections.js"
100
24
  )}";
25
+ import { ${list_action}URL, ${edit_action_name}URL } from "${importPath(
26
+ "src/back/routes/urls.js"
27
+ )}";
28
+ import {tempstream} from "tempstream";
101
29
 
102
- import { ${list_action}URL, ${toPascalCase(
103
- edit_action_name
104
- )}URL } from "${importPath("src/back/routes/urls.js")}";
30
+ ${Array.from(
31
+ new Set(field_handler_results.map(({ imports }) => imports.edit))
32
+ ).join("\n")}
105
33
 
106
- import {tempstream} from "tempstream";
34
+ ${collection_fields.filter((e) => !e.is_required).length ? `import {withFallback} from "@sealcode/sealgen";` : ""}
107
35
  `,
108
36
  fields: `...${toPascalCase(collection_name)}FormFields`,
109
37
  controls: `
@@ -130,18 +58,18 @@ async function editItemFormTemplate(edit_action_name, newfilefullpath, collectio
130
58
  throw new Error("Error when parsing the form values");
131
59
  }
132
60
 
133
- ${collection_fields.map((field) => renderFieldPreEditAction(field)).filter((e) => e !== "").join("\n")}
61
+ ${field_handler_results.map((result) => result.pre_edit).filter((e) => e !== "").join("\n")}
134
62
 
135
63
  item.setMultiple(
136
64
  {
137
- ${collection_fields.map((field) => renderFieldForSealious(field)).filter((e) => e != "").join(",\n")}
65
+ ${field_handler_results.filter((r) => !r.hide_sealious_value).map((result) => result.sealious_value(result)).filter((e) => e != "").join(",\n")}
138
66
  }
139
67
  );
140
68
  await item.save(ctx.$context);
141
69
 
142
70
 
143
- // ## POST-EDIT ACTIONS ############
144
- ${collection_fields.map((field) => renderFieldPostEditAction(field)).filter((e) => e != "").join("\n")}
71
+ ${field_handler_results.map((result) => result.post_edit).filter((e) => e != "").length ? `// #### POST-EDIT ACTIONS ############` : ""}
72
+ ${field_handler_results.map((result) => result.post_edit).filter((e) => e != "").join("\n")}
145
73
  }
146
74
  `,
147
75
  other_methods: `
@@ -150,9 +78,9 @@ async function editItemFormTemplate(edit_action_name, newfilefullpath, collectio
150
78
  // of the resource to edit.
151
79
  // If sealgen got it wrong, just return the param name that should be used here instead
152
80
  const param_name =
153
- ${toPascalCase(edit_action_name)}URL.params
81
+ ${edit_action_name}URL.params
154
82
  .find(param => param.toLowerCase().includes("id"))
155
- || ${toPascalCase(edit_action_name)}URL.params[0]
83
+ || ${edit_action_name}URL.params[0]
156
84
  const id = ctx.params[param_name];
157
85
  if (!id) {
158
86
  throw new Error("Missing URL parameter: " + param_name);
@@ -176,14 +104,43 @@ async function editItemFormTemplate(edit_action_name, newfilefullpath, collectio
176
104
  }
177
105
 
178
106
  return {
179
- ${collection_fields.map((field) => renderFieldForInitialValues(field)).filter((e) => e !== "").join(",\n")}
107
+ ${field_handler_results.filter((r) => !r.hide_initial_value_edit).map(({ initial_value_edit, is_required, name }) => {
108
+ if (typeof initial_value_edit == "string") {
109
+ if (initial_value_edit == "") {
110
+ return "";
111
+ }
112
+ return `${name}: ${initial_value_edit}`;
113
+ }
114
+ const main_value = initial_value_edit.main_value;
115
+ if (!main_value) {
116
+ throw new Error(
117
+ "Missing main_value for initial_value_edit"
118
+ );
119
+ }
120
+ const { parsed_value, fallback_value } = initial_value_edit;
121
+ if (is_required) {
122
+ return `${name}: ${parsed_value || main_value}`;
123
+ }
124
+ if (main_value && parsed_value && fallback_value) {
125
+ if (main_value == parsed_value) {
126
+ return `${name}: withFallback(${main_value}, ${fallback_value})`;
127
+ } else {
128
+ return `${name}: withFallback(${main_value}, ${parsed_value}, ${fallback_value})`;
129
+ }
130
+ } else {
131
+ console.error(initial_value_edit);
132
+ throw new Error(
133
+ "Missing field info for generating initial value"
134
+ );
135
+ }
136
+ }).filter((e) => e.trim() !== "").join(",\n")}
180
137
  };
181
138
  }
182
139
  `,
183
140
  render: ` async render(ctx: Context, data: FormData, show_field_errors: boolean) {
184
141
  return html(
185
142
  ctx,
186
- "AddBlogArticle",
143
+ "Edit ${toPascalCase(collection_name)}",
187
144
  tempstream/* HTML */ \` <div class="add-opinion-form">
188
145
  <a class="" href="\${${list_action}URL}"
189
146
  >\u2190 Back to ${collection_name} list</a
@@ -6,6 +6,15 @@ import { DefaultListFilters } from "../../page/default-list-filters.js";
6
6
  import { toKebabCase, toPascalCase } from "js-convert-case";
7
7
  import { formatWithPrettier } from "../../utils/prettier.js";
8
8
  const target_locreq = _locreq(process.cwd());
9
+ function fieldImport({ type }, importPath) {
10
+ if (type == "image") {
11
+ return `
12
+ import type { FilePointer } from "@sealcode/file-manager";
13
+ import { imageRouter } from "${importPath("src/back/image-router.js")}";
14
+ `;
15
+ }
16
+ return "";
17
+ }
9
18
  function makeDefaultFilterFields(collection_fields) {
10
19
  return collection_fields.map(
11
20
  (field) => ` { field: "${field.name}", ...${DefaultListFilters[field.type] ? `DefaultListFilters["${field.type}"]` : `DefaultListFilters.fallback`} }`
@@ -51,7 +60,7 @@ const default_hooks = {
51
60
  pre_header_html: "",
52
61
  post_header_html: "",
53
62
  post_import_js: "",
54
- render_item: (collection_name) => ` async renderItem(ctx: Context, item: CollectionItem<typeof ${toPascalCase(
63
+ render_item: (collection_name) => ` async renderItem(_ctx: Context, item: CollectionItem<typeof ${toPascalCase(
55
64
  collection_name
56
65
  )}>) {
57
66
  return <tr>
@@ -74,8 +83,13 @@ async function collectionListTemplate(collection_name, action_name, newfilefullp
74
83
  extract_fields_from_collection(collection_name)
75
84
  ]);
76
85
  const importPath = curryImportPath(newfilefullpath);
86
+ const field_import_string = Array.from(
87
+ new Set(
88
+ collection_fields.map((field) => fieldImport(field, importPath))
89
+ )
90
+ ).join("\n");
77
91
  const result = `import type { Context } from "koa";
78
- import type { CollectionItem, ItemListResult } from "sealious";
92
+ import type { CollectionItem } from "sealious";
79
93
  import type { FlatTemplatable, Templatable } from "tempstream";
80
94
  import { TempstreamJSX, tempstream } from "tempstream";
81
95
  import { ${uppercase_collection} } from "${importPath(
@@ -89,12 +103,9 @@ import {
89
103
  DefaultListFilters,
90
104
  } from "@sealcode/sealgen";
91
105
  import qs from "qs";
92
- import type { FilePointer } from "@sealcode/file-manager";
93
- import { imageRouter } from "${importPath(
94
- target_locreq.resolve("src/back/image-router.ts")
95
- )}";
96
106
 
97
107
  ${hooks.post_import_js}
108
+ ${field_import_string}
98
109
 
99
110
  export const actionName = "${action_name}";
100
111
 
@@ -107,6 +118,8 @@ const filterFields = [
107
118
  prepareValue?: (filter_value: unknown)=>unknown; // set this function to change what filter value is passed to Sealious
108
119
  }[];
109
120
 
121
+
122
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
110
123
  const displayFields = [
111
124
  ${makeDefaultDisplayFields(collection_fields)}
112
125
  ] as {
@@ -152,10 +165,12 @@ export default new (class ${action_name}Page extends SealiousItemListPage<
152
165
  // adding opportunity to adjust the values for a given field filter before it's sent to Sealious
153
166
  const values = await super.getFilterValues(ctx);
154
167
  for (const filterField of filterFields) {
168
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
155
169
  const key = filterField.field as keyof typeof values;
156
170
  if (key in values) {
157
171
  const prepare_fn = filterField.prepareValue;
158
172
  if (prepare_fn) {
173
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
159
174
  values[key] = prepare_fn(values[key]) as any;
160
175
  }
161
176
  }
@@ -11,22 +11,24 @@ import { ${delete_action_name}URL } from "${importPath(
11
11
  "src/back/routes/urls.js"
12
12
  )}";
13
13
  import html from "${importPath("src/back/html.ts")}";
14
- import { ${toPascalCase(collection_name)} } from "${importPath(
15
- "src/back/collections/collections.js"
16
- )}";
14
+ import { ${toPascalCase(collection_name)} as ${toPascalCase(
15
+ collection_name
16
+ )}Collection } from "${importPath("src/back/collections/collections.js")}";
17
17
 
18
18
  export const actionName = "${ask_delete_action_name}";
19
19
 
20
20
  export default new (class ${ask_delete_action_name}Page extends Page {
21
21
  canAccess = async (ctx: Context) => {
22
- const policy = ${toPascalCase(collection_name)}.getPolicy("edit");
22
+ const policy = ${toPascalCase(
23
+ collection_name
24
+ )}Collection.getPolicy("edit");
23
25
  const response = await policy.check(ctx.$context);
24
26
  return { canAccess: response?.allowed || false, message: response?.reason || "" };
25
27
  };
26
28
 
27
29
  getItemTitle(_ctx: Context, item: CollectionItem<typeof ${toPascalCase(
28
30
  collection_name
29
- )}>){
31
+ )}Collection>){
30
32
  return item.get("name")
31
33
  }
32
34
 
@@ -0,0 +1,14 @@
1
+ const boolean_handler = async (_, { name }, ___, { is_required_expr, fields_var }) => ({
2
+ field: `${name}: new Fields.Boolean(${is_required_expr})`,
3
+ controls: `new Controls.Checkbox(${fields_var}.${name}, { label: "${name}" })`,
4
+ initial_value_edit: {
5
+ fallback_value: `"false"`,
6
+ parsed_value: `String(item.get("${name}"))`
7
+ },
8
+ main_value_sealious: `!!data.${name}`,
9
+ fallback_value_sealious: "false"
10
+ });
11
+ export {
12
+ boolean_handler
13
+ };
14
+ //# sourceMappingURL=boolean.js.map
@@ -0,0 +1,8 @@
1
+ const date_handler = async (_, { name }, ___, { fields_var }) => ({
2
+ controls: `new Controls.SimpleInput(${fields_var}.${name}, { label: "${name}", type: "date"})`,
3
+ fallback_value_sealious: `undefined`
4
+ });
5
+ export {
6
+ date_handler
7
+ };
8
+ //# sourceMappingURL=date.js.map
@@ -0,0 +1,209 @@
1
+ import { toKebabCase, toPascalCase } from "js-convert-case";
2
+ import extract_fields_from_collection from "../../../utils/extract-fields-from-collection.js";
3
+ const deep_reverse_single_reference_handler = async (collection_name, {
4
+ name,
5
+ target,
6
+ referencing_collection,
7
+ intermediary_field_that_points_there,
8
+ referencing_field
9
+ }, ___, { is_required_expr, fields_var }) => {
10
+ if (!referencing_collection || !target || !intermediary_field_that_points_there || !referencing_field) {
11
+ console.warn(
12
+ "missing referencing_collection, target, intermediary_field_that_points_there, or referencing_field member in extracted field info"
13
+ );
14
+ return { field: "" };
15
+ }
16
+ const referencing_fields = await extract_fields_from_collection(
17
+ referencing_collection
18
+ );
19
+ if (referencing_fields.length == 2) {
20
+ const post_create = (get_id) => `{
21
+ const item_id = ${get_id};
22
+ // First, we remove links refering to current item
23
+ const {items: existing_links } = (await ctx.$app.collections["${referencing_collection}"]
24
+ .list(ctx.$context)
25
+ .filter({"${referencing_field}": item_id})
26
+ .fetch()
27
+ )
28
+ const should_exist_ids = Object.entries(
29
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
30
+ (data["${name}"] as unknown as Record<string, string>) || {}
31
+ )
32
+ .filter(([, value]) => value === "on")
33
+ .map(([key]) => key);
34
+ const to_delete = existing_links.filter(
35
+ (link) => !should_exist_ids.includes(link.get("${intermediary_field_that_points_there}"))
36
+ );
37
+ const to_create_ids = should_exist_ids.filter(
38
+ (id) => !existing_links.map(({ id }) => id).includes(id)
39
+ );
40
+ await Promise.all(to_delete.map((item) => item.remove(ctx.$context)));
41
+ // Then, we iterate over all selected values and insert appropriate missing links
42
+ const promises = to_create_ids.map(async (id) => {
43
+ await ctx.$app.collections["${toKebabCase(referencing_collection)}"].create(
44
+ ctx.$context,
45
+ {
46
+ "${referencing_field}": item_id,
47
+ "${intermediary_field_that_points_there}": id,
48
+ }
49
+ );
50
+ });
51
+ await Promise.all(promises);
52
+ }`;
53
+ return {
54
+ field: `${name}: new Fields.CheckboxedListField(${is_required_expr}, async (ctx) => {
55
+ const { items } = await ctx.$app.collections["${target}"]
56
+ .list(ctx.$context)
57
+ .fetch();
58
+
59
+ return items.map((s) => ({
60
+ label: s.get("name"),
61
+ value: s.id,
62
+ }));
63
+ })`,
64
+ controls: `new Controls.CheckboxedListInput(${fields_var}.${name}, {
65
+ label: "${name}",
66
+ })`,
67
+ post_edit: post_create(`await this.getID(ctx)`),
68
+ post_create: post_create(`created.id`),
69
+ initial_value_edit: `Object.fromEntries((item.get("${name}") || []).map(id=>[id, "on"]))`,
70
+ sealious_value: () => ""
71
+ };
72
+ } else {
73
+ const initial_value_function_name = `get${toPascalCase(
74
+ name
75
+ )}TableInitialValues`;
76
+ const post_create = (get_id) => `{
77
+ const item_id = ${get_id};
78
+ // First, we remove links refering to current item
79
+ const { items: to_delete } = await ctx.$app.collections[
80
+ "${toKebabCase(referencing_collection)}"
81
+ ]
82
+ .list(ctx.$context)
83
+ .filter({ "${referencing_field}": item_id })
84
+ .fetch();
85
+ await Promise.all(to_delete.map((item) => item.remove(ctx.$context)));
86
+
87
+ // Then, we iterate over all selected values and insert appropriate entries for the subcategories relation
88
+
89
+ const promises = data["${name}"]
90
+ .filter((s) => s.selected == true)
91
+ .map(async (link_info) => {
92
+ await ctx.$app.collections["${toKebabCase(referencing_collection)}"].create(
93
+ ctx.$context,
94
+ {
95
+ "${referencing_field}": item_id,
96
+ "${intermediary_field_that_points_there}": String(link_info["${intermediary_field_that_points_there}"]),
97
+ ${referencing_fields.filter(
98
+ ({ type }) => type !== "single-reference"
99
+ ).map(
100
+ (field) => `"${field.name}": link_info["${field.name}"]`
101
+ ).join(",\n")}
102
+ }
103
+ );
104
+ });
105
+
106
+ await Promise.all(promises);
107
+ }`;
108
+ const initial_value = `(await ${initial_value_function_name}(ctx,item)) as unknown as FormDataValue`;
109
+ return {
110
+ imports: {
111
+ shared: `import type { CollectionItem } from "sealious"; import type { Context } from "koa";`,
112
+ edit: `import {${initial_value_function_name}} from "../shared.js"; import type { FormDataValue } from "@sealcode/sealgen";`
113
+ },
114
+ field: `
115
+ ${name}: new Fields.Table({
116
+ selected: new Fields.Boolean(false),
117
+ "${referencing_field}": new Fields.SimpleFormField(true),
118
+ "${intermediary_field_that_points_there}": new Fields.SimpleFormField(true),
119
+ ${referencing_fields.filter(({ type }) => type !== "single-reference").map(
120
+ (field) => `${field.name}: new Fields.SimpleFormField(true)`
121
+ ).join(",\n")}
122
+ })`,
123
+ controls: `
124
+ new Controls.Table(${fields_var}.${name}, {
125
+ render_fields: {
126
+ selected: (fctx, name, value) => /* HTML */ \`<input
127
+ type="checkbox"
128
+ name=\${name}
129
+ \${value?.toString() == "true" ? "checked" : ""}
130
+ form="\${fctx.form_id}"
131
+ autocomplete="off"
132
+ />\`,
133
+ "${referencing_field}": (fctx, name, value) => /* HTML */ \`<input
134
+ type="hidden"
135
+ name=\${name}
136
+ value="\${value}"
137
+ form="\${fctx.form_id}"
138
+ />\`,
139
+ "${intermediary_field_that_points_there}": (fctx, name, value) => /* HTML */ \`<input
140
+ type="hidden"
141
+ name=\${name}
142
+ value="\${value}"
143
+ form="\${fctx.form_id}"
144
+ />\`,
145
+ ${referencing_fields.filter(
146
+ ({ type }) => type !== "single-reference"
147
+ ).map(
148
+ (field) => `${field.name}: (fctx, name, value) => /* HTML */ \`<input
149
+ name=\${name}
150
+ value="\${value}"
151
+ form="\${fctx.form_id}"
152
+ autocomplete="off"
153
+ />\``
154
+ ).join(",\n")}
155
+ },
156
+ allow_adding: false,
157
+ })`,
158
+ top_level_shared: `export async function ${initial_value_function_name}(
159
+ ctx: Context,
160
+ item: CollectionItem<typeof ${toPascalCase(
161
+ collection_name
162
+ )}>
163
+ ) {
164
+ // convert from an array to array-like object
165
+ return Object.fromEntries(
166
+ await Promise.all([
167
+ ctx.$app.collections["${toKebabCase(target)}"].list(ctx.$context).fetch(),
168
+ ctx.$app.collections["${toKebabCase(
169
+ referencing_collection
170
+ )}"].list(ctx.$context).fetch(),
171
+ ]).then(([{ items: targets }, { items: links }]) =>
172
+ targets
173
+ .map((target) => {
174
+ return {
175
+ selected: (item.get("${name}") || []).includes(
176
+ target.id
177
+ )
178
+ ? "on"
179
+ : "off",
180
+ "${referencing_field}": item.id,
181
+ "${intermediary_field_that_points_there}": target.id,
182
+ ${referencing_fields.filter(
183
+ ({ type }) => type !== "single-reference"
184
+ ).map(
185
+ (field) => `"${field.name}": links
186
+ .find(
187
+ (e) =>
188
+ e.get("${intermediary_field_that_points_there}") == target.id &&
189
+ e.get("${referencing_field}") == item.id
190
+ )
191
+ ?.get("rank") || 0,`
192
+ ).join(",\n")}
193
+ };
194
+ })
195
+ .entries()
196
+ )
197
+ );
198
+ }`,
199
+ initial_value_edit: initial_value,
200
+ initial_value_create: initial_value,
201
+ post_edit: post_create(`await this.getID(ctx)`),
202
+ post_create: post_create(`created.id`)
203
+ };
204
+ }
205
+ };
206
+ export {
207
+ deep_reverse_single_reference_handler
208
+ };
209
+ //# sourceMappingURL=deep-reverse-single-reference.js.map
@@ -0,0 +1,50 @@
1
+ import { toPascalCase } from "js-convert-case";
2
+ const default_handler = async (collection_name, { name, is_readonly, is_required }) => {
3
+ const required_s = `${toPascalCase(
4
+ collection_name
5
+ )}.fields.${name}.required`;
6
+ const fields_var = `${toPascalCase(collection_name)}FormFields`;
7
+ const fallback_value_initial = `""`;
8
+ const fallback_value_sealious = `""`;
9
+ const main_value_initial = `item.get("${name}")`;
10
+ const main_value_sealious = `data["${name}"]`;
11
+ const parsed_value = main_value_initial;
12
+ return {
13
+ is_required,
14
+ name,
15
+ post_create: "",
16
+ imports: { shared: "", edit: "" },
17
+ hide_field: is_readonly,
18
+ field: `${name}: new Fields.CollectionField(${required_s}, ${toPascalCase(
19
+ collection_name
20
+ )}.fields.${name})`,
21
+ hide_control: is_readonly,
22
+ controls: `new Controls.SimpleInput(${fields_var}.${name}, { label: "${name}"})`,
23
+ is_required_expr: `${toPascalCase(
24
+ collection_name
25
+ )}.fields.${name}.required`,
26
+ fields_var,
27
+ top_level_shared: "",
28
+ hide_initial_value_edit: is_readonly,
29
+ initial_value_edit: {
30
+ fallback_value: fallback_value_initial,
31
+ main_value: main_value_initial,
32
+ parsed_value
33
+ },
34
+ hide_initial_value_create: is_readonly,
35
+ initial_value_create: "",
36
+ pre_edit: "",
37
+ post_edit: "",
38
+ pre_create: "",
39
+ main_value_sealious,
40
+ fallback_value_sealious,
41
+ sealious_value: (result) => {
42
+ return result.is_required ? `${result.name}: ${result.main_value_sealious}` : `${result.name}: ${result.main_value_sealious} != null ? ${result.main_value_sealious} : ${result.fallback_value_sealious}`;
43
+ },
44
+ hide_sealious_value: is_readonly
45
+ };
46
+ };
47
+ export {
48
+ default_handler
49
+ };
50
+ //# sourceMappingURL=default.js.map
@@ -0,0 +1,19 @@
1
+ import { toPascalCase } from "js-convert-case";
2
+ const enum_handler = async (collection_name, { name }, _, { fields_var }) => ({
3
+ // this assertion and import is only necessary because required fields
4
+ // in Sealious lose the type information. Once this is fixed, we can remove this import here
5
+ imports: { shared: `import type { FieldTypes } from "sealious";` },
6
+ field: `${name}: new Fields.PickFromListField(
7
+ ${toPascalCase(collection_name)}.fields["${name}"].required,
8
+ async (ctx) =>
9
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10
+ (${toPascalCase(collection_name)}.fields["${name}"])
11
+ .getAllowedValues(ctx.$app)
12
+ .map((value) => ({ value, label: value }))
13
+ )`,
14
+ controls: `new Controls.Dropdown(${fields_var}.${name}, { label: "${name}"})`
15
+ });
16
+ export {
17
+ enum_handler
18
+ };
19
+ //# sourceMappingURL=enum.js.map
@@ -0,0 +1,18 @@
1
+ import { toCamelCase } from "js-convert-case";
2
+ const file_handler = async (_, { name, is_required }, __, { is_required_expr }) => {
3
+ const tempname = toCamelCase("prepared-" + name);
4
+ return {
5
+ field: `${name}: new Fields.File(${is_required_expr}, TheFileManager)`,
6
+ initial_value_edit: `${name}: {old: item.get("${name}")}`,
7
+ pre_edit: `const ${tempname} = data.${name}.new || data.${name}.old;
8
+ if(!${tempname}){
9
+ throw new Error("Missing field: ${name}")
10
+ }`,
11
+ fallback_value_sealious: "null",
12
+ ...is_required ? { main_value_sealious: tempname } : {}
13
+ };
14
+ };
15
+ export {
16
+ file_handler
17
+ };
18
+ //# sourceMappingURL=file.js.map
@@ -0,0 +1,32 @@
1
+ import { toCamelCase } from "js-convert-case";
2
+ const image_handler = async (_, { name, is_required }, importPath, { is_required_expr, fields_var }) => {
3
+ const tempname = toCamelCase("prepared-" + name);
4
+ return {
5
+ imports: {
6
+ shared: `import { imageRouter } from "${importPath(
7
+ "src/back/image-router.js"
8
+ )}";
9
+ import { TheFileManager } from "${importPath(
10
+ "src/back/file-manager.js"
11
+ )}";`
12
+ },
13
+ field: `${name}: new Fields.File(${is_required_expr}, TheFileManager)`,
14
+ controls: `new Controls.Photo(${fields_var}.${name}, imageRouter, {
15
+ label: "${name}",
16
+ })`,
17
+ initial_value_edit: `{old: item.get("${name}")}`,
18
+ pre_edit: `const ${tempname} = data.${name}.new || data.${name}.old ` + (is_required ? `; if(!${tempname}){
19
+ throw new Error("Missing field: ${name}")
20
+ }` : " || undefined;"),
21
+ pre_create: `const ${tempname} = data.${name}.new || data.${name}.old;
22
+ if(!${tempname}){
23
+ throw new Error("Missing field: ${name}")
24
+ }`,
25
+ fallback_value_sealious: "null",
26
+ sealious_value: () => `${name}: ${tempname}`
27
+ };
28
+ };
29
+ export {
30
+ image_handler
31
+ };
32
+ //# sourceMappingURL=image.js.map