@sealcode/sealgen 0.15.2 → 0.15.4
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.cjs +38 -0
- package/.xunit +53 -0
- package/@types/src/page/stateful-page.d.ts +4 -2
- package/build.js +1 -1
- package/{dist → lib}/add-route.test.js +2 -2
- package/{dist → lib}/build.js +3 -3
- package/{dist → lib}/builder.js +3 -0
- package/lib/cli.js +39 -0
- package/{dist → lib}/esbuild-plugins/load-assets.js +2 -2
- package/{dist → lib}/forms/controls/array.js +1 -2
- package/{dist → lib}/forms/controls/autocomplete.js +8 -10
- package/{dist → lib}/forms/controls/checkboxed-list-input.test.js +3 -2
- package/{dist → lib}/forms/controls/collapsible-field-group.js +2 -1
- package/{dist → lib}/forms/controls/editable-collection-subset.js +6 -4
- package/{dist → lib}/forms/controls/field-group.js +1 -1
- package/{dist → lib}/forms/controls/file.js +2 -5
- package/{dist → lib}/forms/controls/form-control.js +2 -1
- package/{dist → lib}/forms/controls/photo.js +1 -1
- package/{dist → lib}/forms/controls/photo.test.js +5 -7
- package/{dist → lib}/forms/controls/textarea.js +1 -1
- package/{dist → lib}/forms/fields/boolean.test.js +0 -1
- package/{dist → lib}/forms/fields/field.js +2 -5
- package/{dist → lib}/forms/fields/file.js +8 -2
- package/{dist → lib}/forms/fields/structured-array.js +1 -1
- package/{dist → lib}/forms/form.test.js +8 -9
- package/{dist → lib}/forms/multiform.test.js +14 -14
- package/{dist → lib}/generate-components.js +1 -1
- package/{dist → lib}/generate-routes.test.js +1 -0
- package/{dist → lib}/generate-stimulus.js +1 -1
- package/{dist → lib}/mount.js +4 -3
- package/{dist → lib}/notifier.js +3 -1
- package/{dist → lib}/page/list.js +1 -1
- package/{dist → lib}/page/mountable-with-fields.js +1 -1
- package/{dist → lib}/page/sealious-list.js +2 -2
- package/{dist → lib}/page/stateful-page.js +34 -17
- package/{dist → lib}/templates/jdd-component.js +2 -2
- package/{dist → lib}/utils/extract-fields-from-collection.js +16 -1
- package/package.json +20 -15
- package/src/add-route.test.ts +2 -2
- package/src/add-route.ts +2 -2
- package/src/build.ts +3 -3
- package/src/builder.ts +3 -2
- package/src/esbuild-plugins/load-assets.ts +5 -4
- package/src/esbuild-plugins/rewrite-asset-imports.ts +4 -3
- package/src/forms/controls/array.ts +5 -3
- package/src/forms/controls/autocomplete.ts +2 -2
- package/src/forms/controls/checkbox-group.ts +1 -5
- package/src/forms/controls/checkboxed-list-input.test.ts +3 -2
- package/src/forms/controls/checkboxed-list-input.ts +0 -1
- package/src/forms/controls/collapsible-field-group.ts +4 -2
- package/src/forms/controls/derived.ts +1 -1
- package/src/forms/controls/dropdown.ts +3 -3
- package/src/forms/controls/editable-collection-subset.ts +10 -4
- package/src/forms/controls/field-group.ts +1 -1
- package/src/forms/controls/file.ts +1 -1
- package/src/forms/controls/form-control.ts +2 -1
- package/src/forms/controls/hidden-input.ts +0 -2
- package/src/forms/controls/multiple-files.ts +8 -5
- package/src/forms/controls/number.ts +1 -1
- package/src/forms/controls/photo.test.ts +6 -7
- package/src/forms/controls/photo.ts +3 -6
- package/src/forms/controls/simple-input.ts +3 -5
- package/src/forms/controls/textarea.ts +3 -8
- package/src/forms/controls/tickable.ts +2 -4
- package/src/forms/fields/boolean.test.ts +0 -1
- package/src/forms/fields/existing-sealious-value.ts +1 -0
- package/src/forms/fields/field.ts +5 -8
- package/src/forms/fields/file.ts +13 -6
- package/src/forms/fields/structured-array.ts +2 -2
- package/src/forms/form.test.ts +9 -10
- package/src/forms/form.ts +1 -1
- package/src/forms/multiform.test.ts +14 -14
- package/src/forms/multiform.ts +2 -1
- package/src/generate-components.ts +1 -2
- package/src/generate-routes.test.ts +1 -0
- package/src/generate-stimulus.ts +1 -1
- package/src/get-fonts.ts +2 -0
- package/src/mount.ts +8 -3
- package/src/notifier.ts +3 -1
- package/src/page/list.ts +1 -1
- package/src/page/mountable-with-fields.ts +3 -1
- package/src/page/sealious-list.ts +3 -2
- package/src/page/stateful-page.ts +50 -22
- package/src/templates/jdd-component.ts +2 -2
- package/src/utils/extract-fields-from-collection.ts +19 -1
- package/src/utils/translate.ts +1 -1
- package/src/utils/utils.ts +2 -0
- package/test.cjs +54 -0
- package/tsconfig.json +3 -4
- package/dist/back/routes/sample.page.js +0 -17
- package/dist/back/routes/sample.test.js +0 -39
- package/dist/hehe.test.js +0 -7
- package/dist/sample-index.js +0 -3
- package/dist/sample-lib.js +0 -9
- package/dist/vitest.config.js +0 -23
- package/src/vitest.config.ts +0 -21
- /package/{dist → lib}/add-collection.js +0 -0
- /package/{dist → lib}/add-jdd-component.js +0 -0
- /package/{dist → lib}/add-route.js +0 -0
- /package/{dist → lib}/components/components.js +0 -0
- /package/{dist → lib}/components/tabs.js +0 -0
- /package/{dist → lib}/components/toggle-modal.js +0 -0
- /package/{dist → lib}/embeddable-file-extensions.js +0 -0
- /package/{dist → lib}/esbuild-plugins/rewrite-asset-imports.js +0 -0
- /package/{dist → lib}/example.test.js +0 -0
- /package/{dist → lib}/find-css-includes.js +0 -0
- /package/{dist → lib}/forms/controls/big-radio-group.js +0 -0
- /package/{dist → lib}/forms/controls/checkbox-group.js +0 -0
- /package/{dist → lib}/forms/controls/checkbox.js +0 -0
- /package/{dist → lib}/forms/controls/checkboxed-list-input.js +0 -0
- /package/{dist → lib}/forms/controls/collapsible-checkbox-group.js +0 -0
- /package/{dist → lib}/forms/controls/controls.js +0 -0
- /package/{dist → lib}/forms/controls/derived-frame.js +0 -0
- /package/{dist → lib}/forms/controls/derived.js +0 -0
- /package/{dist → lib}/forms/controls/dropdown.js +0 -0
- /package/{dist → lib}/forms/controls/form-field-control.js +0 -0
- /package/{dist → lib}/forms/controls/form-header.js +0 -0
- /package/{dist → lib}/forms/controls/form-paragraph.js +0 -0
- /package/{dist → lib}/forms/controls/frame.js +0 -0
- /package/{dist → lib}/forms/controls/hidden-input.js +0 -0
- /package/{dist → lib}/forms/controls/html.js +0 -0
- /package/{dist → lib}/forms/controls/multiple-files.js +0 -0
- /package/{dist → lib}/forms/controls/multiple-photos.js +0 -0
- /package/{dist → lib}/forms/controls/number.js +0 -0
- /package/{dist → lib}/forms/controls/radio.js +0 -0
- /package/{dist → lib}/forms/controls/reverse-single-reference.js +0 -0
- /package/{dist → lib}/forms/controls/simple-input.js +0 -0
- /package/{dist → lib}/forms/controls/tickable.js +0 -0
- /package/{dist → lib}/forms/fields/boolean.js +0 -0
- /package/{dist → lib}/forms/fields/checkboxed-list.js +0 -0
- /package/{dist → lib}/forms/fields/collection-field.js +0 -0
- /package/{dist → lib}/forms/fields/email.js +0 -0
- /package/{dist → lib}/forms/fields/email.test.js +0 -0
- /package/{dist → lib}/forms/fields/enum-multiple.js +0 -0
- /package/{dist → lib}/forms/fields/enum-multiple.test.js +0 -0
- /package/{dist → lib}/forms/fields/enum.js +0 -0
- /package/{dist → lib}/forms/fields/enum.test.js +0 -0
- /package/{dist → lib}/forms/fields/existing-sealious-value.js +0 -0
- /package/{dist → lib}/forms/fields/fields.js +0 -0
- /package/{dist → lib}/forms/fields/multiple-files.js +0 -0
- /package/{dist → lib}/forms/fields/number.js +0 -0
- /package/{dist → lib}/forms/fields/number.test.js +0 -0
- /package/{dist → lib}/forms/fields/pick-from-list.js +0 -0
- /package/{dist → lib}/forms/fields/pick-from-list.test.js +0 -0
- /package/{dist → lib}/forms/fields/proxy-field.js +0 -0
- /package/{dist → lib}/forms/fields/regexp.js +0 -0
- /package/{dist → lib}/forms/fields/regexp.test.js +0 -0
- /package/{dist → lib}/forms/fields/reverse-single-reference.js +0 -0
- /package/{dist → lib}/forms/fields/simple-form-field.js +0 -0
- /package/{dist → lib}/forms/form-types.js +0 -0
- /package/{dist → lib}/forms/form.js +0 -0
- /package/{dist → lib}/forms/multiform.js +0 -0
- /package/{dist → lib}/generate-collections.js +0 -0
- /package/{dist → lib}/generate-routes.js +0 -0
- /package/{dist → lib}/get-fonts.js +0 -0
- /package/{dist → lib}/index.js +0 -0
- /package/{dist → lib}/koa.d.js +0 -0
- /package/{dist → lib}/make-env.js +0 -0
- /package/{dist → lib}/make-hidden-inputs.js +0 -0
- /package/{dist → lib}/page/access-control.js +0 -0
- /package/{dist → lib}/page/default-list-filters.js +0 -0
- /package/{dist → lib}/page/mountable.js +0 -0
- /package/{dist → lib}/page/page.js +0 -0
- /package/{dist → lib}/page/props-parser.js +0 -0
- /package/{dist → lib}/run.js +0 -0
- /package/{dist → lib}/sanitize.js +0 -0
- /package/{dist → lib}/target-locreq.js +0 -0
- /package/{dist → lib}/templates/collection.js +0 -0
- /package/{dist → lib}/templates/form.js +0 -0
- /package/{dist → lib}/templates/list.js +0 -0
- /package/{dist → lib}/templates/long-running-process.js +0 -0
- /package/{dist → lib}/templates/multiform.js +0 -0
- /package/{dist → lib}/templates/page.js +0 -0
- /package/{dist → lib}/templates/post.js +0 -0
- /package/{dist → lib}/templates/redirect.js +0 -0
- /package/{dist → lib}/templates/route-test.js +0 -0
- /package/{dist → lib}/templates/stateful-page.js +0 -0
- /package/{dist → lib}/templates/stimulus-controller.js +0 -0
- /package/{dist → lib}/templates/templates.js +0 -0
- /package/{dist → lib}/test_utils/locator_is_visible.js +0 -0
- /package/{dist → lib}/utils/assert-throws.js +0 -0
- /package/{dist → lib}/utils/base64.js +0 -0
- /package/{dist → lib}/utils/browser-creator.js +0 -0
- /package/{dist → lib}/utils/escape-url-params.js +0 -0
- /package/{dist → lib}/utils/exec.js +0 -0
- /package/{dist → lib}/utils/format-external-url.js +0 -0
- /package/{dist → lib}/utils/import-path.js +0 -0
- /package/{dist → lib}/utils/input-wrapper.js +0 -0
- /package/{dist → lib}/utils/list-collections.js +0 -0
- /package/{dist → lib}/utils/make-slug.js +0 -0
- /package/{dist → lib}/utils/module-dirname.js +0 -0
- /package/{dist → lib}/utils/natural-numbers.js +0 -0
- /package/{dist → lib}/utils/prettier.js +0 -0
- /package/{dist → lib}/utils/question.js +0 -0
- /package/{dist → lib}/utils/render-attributes.js +0 -0
- /package/{dist → lib}/utils/sleep.js +0 -0
- /package/{dist → lib}/utils/translate.js +0 -0
- /package/{dist → lib}/utils/url-with-new-params.js +0 -0
- /package/{dist → lib}/utils/utils.js +0 -0
- /package/{dist → lib}/utils/walk.js +0 -0
|
@@ -72,7 +72,7 @@ function multiform_factory(canAccessFun1, canAccessFun2) {
|
|
|
72
72
|
}
|
|
73
73
|
async function tests(js_enabled) {
|
|
74
74
|
let page;
|
|
75
|
-
|
|
75
|
+
before(async () => {
|
|
76
76
|
const browser = await getBrowser();
|
|
77
77
|
const context = await browser.newContext({
|
|
78
78
|
javaScriptEnabled: js_enabled
|
|
@@ -116,7 +116,7 @@ async function general_form_validation_e2e_tests(js_enabled) {
|
|
|
116
116
|
async function individual_field_validation_e2e_tests(javaScriptEnabled) {
|
|
117
117
|
let browser;
|
|
118
118
|
let page;
|
|
119
|
-
|
|
119
|
+
before(async () => {
|
|
120
120
|
browser = await getBrowser();
|
|
121
121
|
const context = await browser.newContext({
|
|
122
122
|
javaScriptEnabled
|
|
@@ -142,7 +142,7 @@ async function individual_field_validation_e2e_tests(javaScriptEnabled) {
|
|
|
142
142
|
describe("multiform test", () => {
|
|
143
143
|
describe("basic tests", async () => {
|
|
144
144
|
let server;
|
|
145
|
-
|
|
145
|
+
before(async () => {
|
|
146
146
|
const app = new Koa();
|
|
147
147
|
const router = new Router();
|
|
148
148
|
mount(
|
|
@@ -179,7 +179,7 @@ describe("multiform test", () => {
|
|
|
179
179
|
app.use(router.routes()).use(router.allowedMethods());
|
|
180
180
|
server = app.listen(port);
|
|
181
181
|
});
|
|
182
|
-
|
|
182
|
+
after(() => {
|
|
183
183
|
server.close();
|
|
184
184
|
});
|
|
185
185
|
describe("turbolinks + js enabled", async () => tests(true));
|
|
@@ -189,7 +189,7 @@ describe("multiform test", () => {
|
|
|
189
189
|
let page;
|
|
190
190
|
let server;
|
|
191
191
|
describe("allow access to both forms when both canAccess return true", async () => {
|
|
192
|
-
|
|
192
|
+
before(async () => {
|
|
193
193
|
const app = new Koa();
|
|
194
194
|
const router = new Router();
|
|
195
195
|
mount(
|
|
@@ -211,7 +211,7 @@ describe("multiform test", () => {
|
|
|
211
211
|
const context = await browser.newContext();
|
|
212
212
|
page = await context.newPage();
|
|
213
213
|
});
|
|
214
|
-
|
|
214
|
+
after(async () => {
|
|
215
215
|
server.close();
|
|
216
216
|
});
|
|
217
217
|
it("check wether it renders", async () => {
|
|
@@ -241,7 +241,7 @@ describe("multiform test", () => {
|
|
|
241
241
|
});
|
|
242
242
|
});
|
|
243
243
|
describe("deny access to both forms", async () => {
|
|
244
|
-
|
|
244
|
+
before(async () => {
|
|
245
245
|
const app = new Koa();
|
|
246
246
|
const router = new Router();
|
|
247
247
|
mount(
|
|
@@ -263,7 +263,7 @@ describe("multiform test", () => {
|
|
|
263
263
|
const context = await browser.newContext();
|
|
264
264
|
page = await context.newPage();
|
|
265
265
|
});
|
|
266
|
-
|
|
266
|
+
after(async () => {
|
|
267
267
|
server.close();
|
|
268
268
|
});
|
|
269
269
|
it("check wether forms didnt render", async () => {
|
|
@@ -289,7 +289,7 @@ describe("multiform test", () => {
|
|
|
289
289
|
});
|
|
290
290
|
});
|
|
291
291
|
describe("allow access to one form, deny to the other", async () => {
|
|
292
|
-
|
|
292
|
+
before(async () => {
|
|
293
293
|
const app = new Koa();
|
|
294
294
|
const router = new Router();
|
|
295
295
|
mount(
|
|
@@ -311,7 +311,7 @@ describe("multiform test", () => {
|
|
|
311
311
|
const context = await browser.newContext({});
|
|
312
312
|
page = await context.newPage();
|
|
313
313
|
});
|
|
314
|
-
|
|
314
|
+
after(async () => {
|
|
315
315
|
server.close();
|
|
316
316
|
});
|
|
317
317
|
it("check wether form1 renders", async () => {
|
|
@@ -359,7 +359,7 @@ describe("multiform test", () => {
|
|
|
359
359
|
describe("validation e2e", async () => {
|
|
360
360
|
describe("basic tests", async () => {
|
|
361
361
|
let server;
|
|
362
|
-
|
|
362
|
+
before(async () => {
|
|
363
363
|
const app = new Koa();
|
|
364
364
|
const router = new Router();
|
|
365
365
|
mount(
|
|
@@ -417,7 +417,7 @@ describe("multiform test", () => {
|
|
|
417
417
|
app.use(router.routes()).use(router.allowedMethods());
|
|
418
418
|
server = app.listen(port);
|
|
419
419
|
});
|
|
420
|
-
|
|
420
|
+
after(async () => {
|
|
421
421
|
server.close();
|
|
422
422
|
});
|
|
423
423
|
it("js enabled", async () => general_form_validation_e2e_tests(true));
|
|
@@ -425,7 +425,7 @@ describe("multiform test", () => {
|
|
|
425
425
|
});
|
|
426
426
|
describe("field specific validation", async () => {
|
|
427
427
|
let server;
|
|
428
|
-
|
|
428
|
+
before(async () => {
|
|
429
429
|
const app = new Koa();
|
|
430
430
|
const router = new Router();
|
|
431
431
|
const fields = {
|
|
@@ -478,7 +478,7 @@ describe("multiform test", () => {
|
|
|
478
478
|
app.use(router.routes()).use(router.allowedMethods());
|
|
479
479
|
server = app.listen(port);
|
|
480
480
|
});
|
|
481
|
-
|
|
481
|
+
after(async () => {
|
|
482
482
|
server.close();
|
|
483
483
|
});
|
|
484
484
|
describe("js enabled", async () => individual_field_validation_e2e_tests(true));
|
|
@@ -14,7 +14,7 @@ async function generateComponents() {
|
|
|
14
14
|
const component_data = component_names.map(
|
|
15
15
|
(p) => "./" + path.relative(components_dir, p).replace(/\.[tj]sx?$/, ".js")
|
|
16
16
|
).map((relative_path) => {
|
|
17
|
-
const name = relative_path.match(/\/([
|
|
17
|
+
const name = relative_path.match(/\/([^/]+).jdd/)?.[1];
|
|
18
18
|
return { name, relative_path };
|
|
19
19
|
});
|
|
20
20
|
const content = `// DO NOT EDIT! This file is generated automaticaly with 'npm run generate-components'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { generateURL } from "./generate-routes.js";
|
|
2
2
|
import * as prettier from "prettier";
|
|
3
3
|
import { getPrettierConfig } from "./utils/prettier.js";
|
|
4
|
+
import assert from "node:assert";
|
|
4
5
|
describe("generate routes", () => {
|
|
5
6
|
it("handles a url with params", async () => {
|
|
6
7
|
assert.strictEqual(
|
|
@@ -13,7 +13,7 @@ async function generateStimulusControllers() {
|
|
|
13
13
|
const controller_data = controller_names.map(
|
|
14
14
|
(p) => "./" + path.relative(target_locreq.resolve(dirname(output_file)), p).replace(/\.[tj]sx?$/, ".js")
|
|
15
15
|
).map((relative_path) => {
|
|
16
|
-
const name = relative_path.match(/\/([
|
|
16
|
+
const name = relative_path.match(/\/([^/]+).stimulus/)?.[1];
|
|
17
17
|
return { name, relative_path };
|
|
18
18
|
});
|
|
19
19
|
const content = `// DO NOT EDIT! This file is generated automaticaly with 'npm run generate-stimulus'
|
package/{dist → lib}/mount.js
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FileManager } from "@sealcode/file-manager";
|
|
1
2
|
import { Middlewares as SealiousMiddlewares } from "sealious";
|
|
2
3
|
async function handleHtmlPromise(ctx, next) {
|
|
3
4
|
await next();
|
|
@@ -6,7 +7,7 @@ async function handleHtmlPromise(ctx, next) {
|
|
|
6
7
|
}
|
|
7
8
|
ctx.set("content-type", "text/html;charset=utf-8");
|
|
8
9
|
}
|
|
9
|
-
function mount(router, url, mountable, use_dummy_app = false) {
|
|
10
|
+
function mount(router, url, mountable, use_dummy_app = false, file_manager = use_dummy_app ? new FileManager("/tmp", "/uploaded_files") : void 0) {
|
|
10
11
|
const raw_url = typeof url === "string" ? url : url.rawURL;
|
|
11
12
|
const args = use_dummy_app ? [
|
|
12
13
|
async (ctx, next) => {
|
|
@@ -31,11 +32,11 @@ function mount(router, url, mountable, use_dummy_app = false) {
|
|
|
31
32
|
};
|
|
32
33
|
await next();
|
|
33
34
|
},
|
|
34
|
-
SealiousMiddlewares.parseBody(),
|
|
35
|
+
SealiousMiddlewares.parseBody(file_manager),
|
|
35
36
|
handleHtmlPromise
|
|
36
37
|
] : [
|
|
37
38
|
SealiousMiddlewares.extractContext(),
|
|
38
|
-
SealiousMiddlewares.parseBody(),
|
|
39
|
+
SealiousMiddlewares.parseBody(file_manager),
|
|
39
40
|
handleHtmlPromise
|
|
40
41
|
];
|
|
41
42
|
router.use(raw_url, ...args);
|
package/{dist → lib}/notifier.js
RENAMED
|
@@ -10,7 +10,9 @@ function make_notifier(port) {
|
|
|
10
10
|
sockets = sockets.filter((s) => s !== socket);
|
|
11
11
|
});
|
|
12
12
|
});
|
|
13
|
-
console.log(
|
|
13
|
+
console.log(
|
|
14
|
+
"build notifier listening on websocket at port " + port.toString()
|
|
15
|
+
);
|
|
14
16
|
return function notify(message) {
|
|
15
17
|
sockets.forEach((s) => s.send(message));
|
|
16
18
|
};
|
|
@@ -14,7 +14,7 @@ class MountableWithFields extends Mountable {
|
|
|
14
14
|
}
|
|
15
15
|
init() {
|
|
16
16
|
for (const [name, field] of Object.entries(this.fields)) {
|
|
17
|
-
field.init(name);
|
|
17
|
+
void field.init(name);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
makeFormControlContext(ctx, data, validate, field_name_prefix = this.field_names_prefix, form_id = this.form_id) {
|
|
@@ -45,13 +45,13 @@ class SealiousItemListPage extends ListPage {
|
|
|
45
45
|
async getFilterValues(ctx) {
|
|
46
46
|
const query_params = qs.parse(ctx.search.slice(1));
|
|
47
47
|
const _filter_values = query_params?.filter || {};
|
|
48
|
-
|
|
48
|
+
const filter_values = is(
|
|
49
49
|
_filter_values,
|
|
50
50
|
predicates.record(predicates.string, predicates.string)
|
|
51
51
|
) ? _filter_values : {};
|
|
52
52
|
return filter_values;
|
|
53
53
|
}
|
|
54
|
-
async renderItem(_, item,
|
|
54
|
+
async renderItem(_, item, _index) {
|
|
55
55
|
return `<div>${item.id}</div>`;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -3,6 +3,7 @@ import deepmerge from "deepmerge";
|
|
|
3
3
|
import { tempstream } from "tempstream";
|
|
4
4
|
import { from_base64, to_base64 } from "../utils/base64.js";
|
|
5
5
|
import { Mountable } from "./mountable.js";
|
|
6
|
+
import { isPlainObject } from "is-what";
|
|
6
7
|
class StatefulPage extends Mountable {
|
|
7
8
|
async canAccess() {
|
|
8
9
|
return { canAccess: true, message: "" };
|
|
@@ -13,7 +14,8 @@ class StatefulPage extends Mountable {
|
|
|
13
14
|
this.render = async (ctx, state, inputs) => {
|
|
14
15
|
return this.wrapInLayout(
|
|
15
16
|
ctx,
|
|
16
|
-
this.wrapInForm(
|
|
17
|
+
await this.wrapInForm(
|
|
18
|
+
ctx,
|
|
17
19
|
state,
|
|
18
20
|
await original_render(ctx, state, inputs)
|
|
19
21
|
),
|
|
@@ -21,12 +23,16 @@ class StatefulPage extends Mountable {
|
|
|
21
23
|
);
|
|
22
24
|
};
|
|
23
25
|
}
|
|
24
|
-
wrapInForm(state, content) {
|
|
25
|
-
return tempstream`<form
|
|
26
|
+
async wrapInForm(context, state, content) {
|
|
27
|
+
return tempstream`<form
|
|
28
|
+
action="./"
|
|
29
|
+
method="POST"
|
|
30
|
+
enctype="multipart/form-data"
|
|
31
|
+
>
|
|
26
32
|
<input
|
|
27
33
|
name="state"
|
|
28
34
|
type="hidden"
|
|
29
|
-
value="${to_base64(
|
|
35
|
+
value="${to_base64(await this.serializeState(context, state))}"
|
|
30
36
|
/>
|
|
31
37
|
${content}
|
|
32
38
|
</form>`;
|
|
@@ -52,7 +58,7 @@ class StatefulPage extends Mountable {
|
|
|
52
58
|
)}`;
|
|
53
59
|
}
|
|
54
60
|
makeActionButton(_state, action_description, ...args) {
|
|
55
|
-
|
|
61
|
+
const { label, content } = this.extractActionAndLabel(action_description);
|
|
56
62
|
return (
|
|
57
63
|
/* HTML */
|
|
58
64
|
`
|
|
@@ -78,10 +84,20 @@ class StatefulPage extends Mountable {
|
|
|
78
84
|
async preprocessState(values) {
|
|
79
85
|
return values;
|
|
80
86
|
}
|
|
81
|
-
async preprocessOverrides(
|
|
87
|
+
async preprocessOverrides(_state, values) {
|
|
82
88
|
return values;
|
|
83
89
|
}
|
|
90
|
+
async serializeState(_context, state) {
|
|
91
|
+
return JSON.stringify(state);
|
|
92
|
+
}
|
|
93
|
+
async deserializeState(_context, s) {
|
|
94
|
+
const deserialized = JSON.parse(s);
|
|
95
|
+
console.log("stateful-page.ts:183", "deserialized:");
|
|
96
|
+
console.dir(deserialized, { depth: 10 });
|
|
97
|
+
return deserialized;
|
|
98
|
+
}
|
|
84
99
|
async extractState(ctx) {
|
|
100
|
+
console.log("stateful-page.ts:188", "EXTRACT STATE");
|
|
85
101
|
if (!hasShape(
|
|
86
102
|
{
|
|
87
103
|
action: predicates.maybe(predicates.string),
|
|
@@ -100,7 +116,8 @@ class StatefulPage extends Mountable {
|
|
|
100
116
|
)
|
|
101
117
|
);
|
|
102
118
|
const original_state_string = ctx.$body.state;
|
|
103
|
-
const original_state =
|
|
119
|
+
const original_state = await this.deserializeState(
|
|
120
|
+
ctx,
|
|
104
121
|
typeof original_state_string == "string" ? from_base64(original_state_string) : "{}"
|
|
105
122
|
);
|
|
106
123
|
const $body = ctx.$body;
|
|
@@ -110,6 +127,7 @@ class StatefulPage extends Mountable {
|
|
|
110
127
|
state_overrides
|
|
111
128
|
);
|
|
112
129
|
let modified_state = deepmerge(original_state, state_overrides, {
|
|
130
|
+
isMergeableObject: isPlainObject,
|
|
113
131
|
arrayMerge: (target, source, options) => {
|
|
114
132
|
const destination = target.slice();
|
|
115
133
|
source.forEach((item, index) => {
|
|
@@ -156,17 +174,16 @@ class StatefulPage extends Mountable {
|
|
|
156
174
|
router.post(path, async (ctx) => {
|
|
157
175
|
const { action, state, inputs, action_args } = await this.extractState(ctx);
|
|
158
176
|
if (action) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
from_base64(action_args)
|
|
166
|
-
)
|
|
167
|
-
),
|
|
168
|
-
inputs
|
|
177
|
+
const new_state = await this.actions[action](
|
|
178
|
+
state,
|
|
179
|
+
inputs,
|
|
180
|
+
...JSON.parse(
|
|
181
|
+
from_base64(action_args)
|
|
182
|
+
)
|
|
169
183
|
);
|
|
184
|
+
console.log("stateful-page.ts:291", "new_state:");
|
|
185
|
+
console.dir(new_state, { depth: 10 });
|
|
186
|
+
ctx.body = this.render(ctx, new_state, inputs);
|
|
170
187
|
} else {
|
|
171
188
|
ctx.body = this.render(ctx, state, inputs);
|
|
172
189
|
}
|
|
@@ -3,7 +3,7 @@ function jddComponentTemplate(component_name) {
|
|
|
3
3
|
return `import type { FlatTemplatable } from "tempstream";
|
|
4
4
|
import { TempstreamJSX } from "tempstream";
|
|
5
5
|
import type {
|
|
6
|
-
|
|
6
|
+
ExtractStructuredComponentArgumentsParsed,
|
|
7
7
|
JDDContext,
|
|
8
8
|
} from "@sealcode/jdd";
|
|
9
9
|
import { Component, ComponentArguments } from "@sealcode/jdd";
|
|
@@ -24,7 +24,7 @@ export class ${toPascalCase(
|
|
|
24
24
|
{
|
|
25
25
|
title,
|
|
26
26
|
content,
|
|
27
|
-
}:
|
|
27
|
+
}: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
|
|
28
28
|
{ render_markdown }: JDDContext
|
|
29
29
|
): FlatTemplatable {
|
|
30
30
|
return (
|
|
@@ -2,6 +2,7 @@ import _locreq from "locreq";
|
|
|
2
2
|
import { exec } from "./utils.js";
|
|
3
3
|
const target_locreq = _locreq(process.cwd());
|
|
4
4
|
import { promises as fs } from "fs";
|
|
5
|
+
import { is, predicates } from "@sealcode/ts-predicates";
|
|
5
6
|
async function extract_fields_from_collection(collection_name) {
|
|
6
7
|
const extractor_code = `import {default as the_app} from "./app.js";
|
|
7
8
|
const c = new the_app().collections["${collection_name}"];
|
|
@@ -22,7 +23,21 @@ console.log(JSON.stringify(fields));
|
|
|
22
23
|
await fs.writeFile(extractor_code_path, extractor_code);
|
|
23
24
|
const { stdout } = await exec("node", [extractor_code_path]);
|
|
24
25
|
await fs.unlink(extractor_code_path);
|
|
25
|
-
|
|
26
|
+
const ret = JSON.parse(stdout);
|
|
27
|
+
if (!is(
|
|
28
|
+
ret,
|
|
29
|
+
predicates.array(
|
|
30
|
+
predicates.shape({
|
|
31
|
+
name: predicates.string,
|
|
32
|
+
type: predicates.string
|
|
33
|
+
})
|
|
34
|
+
)
|
|
35
|
+
)) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
"Encountered a problem while extracting the names of fields from collection. Got: " + stdout
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return ret;
|
|
26
41
|
}
|
|
27
42
|
export {
|
|
28
43
|
extract_fields_from_collection as default
|
package/package.json
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sealcode/sealgen",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.4",
|
|
4
4
|
"description": "Module to automate adding routes and collections to a sealious application",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "lib/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
+
"prebuild": "npm run lint && rm -rf lib",
|
|
8
9
|
"build": "node ./build.js",
|
|
10
|
+
"pretest": "npm run build",
|
|
11
|
+
"test": "node test.cjs",
|
|
9
12
|
"typecheck": "tsc --noemit",
|
|
10
|
-
"prepare": "
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
13
|
+
"prepare": "npm run build",
|
|
14
|
+
"lint": "eslint src",
|
|
15
|
+
"preinstrument": "npm run build && rm -fr .xunit coverage lib-instrumented",
|
|
16
|
+
"instrument": "npx nyc instrument --exclude \"\" lib lib-instrumented",
|
|
17
|
+
"pretest-reports": "npm run instrument",
|
|
18
|
+
"test-reports": "npx nyc --exclude \"\" ./node_modules/.bin/mocha --recursive --timeout=10000 --require source-map-support/register --reporter xunit --reporter-option output=.xunit 'lib-instrumented/**/*.test.js' --exit && nyc report --reporter clover --exclude \"\"",
|
|
19
|
+
"precover-html": "rm -rf coverate/lcov-report",
|
|
20
|
+
"cover-html": "npm run test-reports && nyc report --reporter lcov --exclude \"\" && xdg-open coverage/lcov-report/index.html"
|
|
18
21
|
},
|
|
19
22
|
"bin": {
|
|
20
23
|
"sealgen": "./dist/cli.js"
|
|
@@ -25,15 +28,15 @@
|
|
|
25
28
|
"@playwright/test": "^1.30.0",
|
|
26
29
|
"@types/koa__router": "^12.0.4",
|
|
27
30
|
"@types/md5": "^2.3.5",
|
|
31
|
+
"@types/mocha": "^10.0.6",
|
|
28
32
|
"@types/node": "^20.8.4",
|
|
29
33
|
"@types/prettier": "^2.7.0",
|
|
30
34
|
"@types/prompts": "^2.0.14",
|
|
31
35
|
"@types/recursive-readdir": "^2.2.4",
|
|
32
36
|
"@types/ws": "^8.5.10",
|
|
33
37
|
"@types/yargs": "^17.0.32",
|
|
34
|
-
"@
|
|
35
|
-
"@
|
|
36
|
-
"@vitest/ui": "^1.1.0",
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
|
39
|
+
"@typescript-eslint/parser": "^5.58.0",
|
|
37
40
|
"axios": "^1.6.7",
|
|
38
41
|
"eslint": "^7.32.0",
|
|
39
42
|
"eslint-config-prettier": "^8.3.0",
|
|
@@ -41,12 +44,13 @@
|
|
|
41
44
|
"eslint-plugin-with-tsc-error": "^0.0.7",
|
|
42
45
|
"get-port": "^5.1.1",
|
|
43
46
|
"koa-body": "^5.0.0",
|
|
47
|
+
"mocha": "^10.4.0",
|
|
48
|
+
"mri": "^1.2.0",
|
|
44
49
|
"nyc": "^15.1.0",
|
|
45
50
|
"playwright": "^1.29.2",
|
|
46
51
|
"source-map-support": "^0.5.21",
|
|
47
52
|
"ts-node": "^10.9.1",
|
|
48
53
|
"typescript": "^4.7",
|
|
49
|
-
"vitest": "^1.1.0",
|
|
50
54
|
"wait-port": "^1.0.4"
|
|
51
55
|
},
|
|
52
56
|
"types": "./@types/src/index.d.ts",
|
|
@@ -58,6 +62,7 @@
|
|
|
58
62
|
"esbuild": "^0.20.0",
|
|
59
63
|
"escape-goat": "^4.0.0",
|
|
60
64
|
"google-fonts-helper": "^3.4.1",
|
|
65
|
+
"is-what": "^4.1.16",
|
|
61
66
|
"js-convert-case": "^4.2.0",
|
|
62
67
|
"locreq": "^3.0.0",
|
|
63
68
|
"md5": "^2.3.0",
|
|
@@ -73,6 +78,6 @@
|
|
|
73
78
|
"peerDependencies": {
|
|
74
79
|
"koa": "^2.13.0",
|
|
75
80
|
"koa-responsive-image-router": "^0.2.24",
|
|
76
|
-
"sealious": "^0.19.
|
|
81
|
+
"sealious": "^0.19.7"
|
|
77
82
|
}
|
|
78
83
|
}
|
package/src/add-route.test.ts
CHANGED
|
@@ -54,7 +54,7 @@ describe("add route test", () => {
|
|
|
54
54
|
it("creates minimal sealious app", async function () {
|
|
55
55
|
console.log("\tcloning sealious-minimal...");
|
|
56
56
|
await execAsync(
|
|
57
|
-
`git clone https://hub.sealcode.org/source/sealious-minimal.git ${app_path} && cd ${app_path} && git checkout
|
|
57
|
+
`git clone https://hub.sealcode.org/source/sealious-minimal.git ${app_path} && cd ${app_path} && git checkout 5b33aac`
|
|
58
58
|
);
|
|
59
59
|
console.log("\tinstalling dependencies and sealgen...");
|
|
60
60
|
await execAsync(
|
|
@@ -102,5 +102,5 @@ describe("add route test", () => {
|
|
|
102
102
|
if (res.status != 200) {
|
|
103
103
|
throw new Error("url generated by add-route exists but doesnt return 200");
|
|
104
104
|
}
|
|
105
|
-
}
|
|
105
|
+
});
|
|
106
106
|
});
|
package/src/add-route.ts
CHANGED
|
@@ -21,7 +21,7 @@ export async function addRoute(
|
|
|
21
21
|
message: 'What\'s the name of the action (e.g. "AddUser"): ',
|
|
22
22
|
validate: (s: string) =>
|
|
23
23
|
s.length > 3 ? true : "Should be at least 3 characters long",
|
|
24
|
-
format: function (action) {
|
|
24
|
+
format: function (action: string) {
|
|
25
25
|
return toPascalCase(action);
|
|
26
26
|
},
|
|
27
27
|
},
|
|
@@ -32,7 +32,7 @@ export async function addRoute(
|
|
|
32
32
|
"Enter a full absolute path for the new route (for example: /admin/users/:id/edit): ",
|
|
33
33
|
validate: (s: string) =>
|
|
34
34
|
s.trim()[0] == "/" ? true : "Should start with a '/'",
|
|
35
|
-
initial: function (_, { action }) {
|
|
35
|
+
initial: function (_, { action }: { action: string }) {
|
|
36
36
|
return "/" + toKebabCase(action);
|
|
37
37
|
},
|
|
38
38
|
},
|
package/src/build.ts
CHANGED
|
@@ -45,19 +45,19 @@ async function build(watch: boolean): Promise<void> {
|
|
|
45
45
|
const port = await get_notifier_port();
|
|
46
46
|
const notifier = make_notifier(port);
|
|
47
47
|
await write_notifier_config(watch, port);
|
|
48
|
-
watcher.on("all",
|
|
48
|
+
watcher.on("all", (_, file_path) => {
|
|
49
49
|
console.log("Detected a change!", file_path);
|
|
50
50
|
builders.forEach((builder) => {
|
|
51
51
|
const owns_a_file = builder.ownsFile(file_path);
|
|
52
52
|
if (owns_a_file) {
|
|
53
|
-
builder.build(notifier);
|
|
53
|
+
void builder.build(notifier);
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
57
|
} else {
|
|
58
58
|
await write_notifier_config(watch);
|
|
59
59
|
builders.forEach((builder) => {
|
|
60
|
-
builder.dispose();
|
|
60
|
+
void builder.dispose();
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
}
|
package/src/builder.ts
CHANGED
|
@@ -16,8 +16,6 @@ export abstract class Builder {
|
|
|
16
16
|
abstract _build(): Promise<void>;
|
|
17
17
|
abstract dispose(): Promise<void>;
|
|
18
18
|
|
|
19
|
-
constructor() {}
|
|
20
|
-
|
|
21
19
|
public ongoing_build: Promise<void> | null = null;
|
|
22
20
|
|
|
23
21
|
public build(notifier?: (message: string) => void) {
|
|
@@ -92,6 +90,7 @@ export class FontsBuilder extends Builder {
|
|
|
92
90
|
return file_path == FONTS_CONFIG_PATH;
|
|
93
91
|
}
|
|
94
92
|
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
95
94
|
async dispose(): Promise<void> {}
|
|
96
95
|
|
|
97
96
|
async _build() {
|
|
@@ -115,6 +114,7 @@ export class BackendTSBuilder extends Builder {
|
|
|
115
114
|
);
|
|
116
115
|
}
|
|
117
116
|
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
118
118
|
async dispose(): Promise<void> {}
|
|
119
119
|
|
|
120
120
|
async _build(): Promise<void> {
|
|
@@ -160,6 +160,7 @@ export class FrontendTSBuilder extends Builder {
|
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
163
164
|
async dispose(): Promise<void> {}
|
|
164
165
|
|
|
165
166
|
async _build(): Promise<void> {
|
|
@@ -5,6 +5,7 @@ import path, { basename } from "node:path";
|
|
|
5
5
|
|
|
6
6
|
import _locreq from "locreq";
|
|
7
7
|
import { embeddable_file_extensions } from "../embeddable-file-extensions.js";
|
|
8
|
+
import { PluginBuild } from "esbuild";
|
|
8
9
|
const target_locreq = _locreq(process.cwd());
|
|
9
10
|
|
|
10
11
|
function addSuffix(filename: string, suffix: string) {
|
|
@@ -12,7 +13,7 @@ function addSuffix(filename: string, suffix: string) {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
async function makeResourceURL(file_path: string) {
|
|
15
|
-
const hash_base = file_path + (await fs.stat(file_path)).mtimeMs;
|
|
16
|
+
const hash_base = file_path + (await fs.stat(file_path)).mtimeMs.toString();
|
|
16
17
|
const hash = md5(hash_base);
|
|
17
18
|
const public_url =
|
|
18
19
|
"dist/" + addSuffix(basename(file_path), "-" + hash.slice(0, 8));
|
|
@@ -26,15 +27,15 @@ async function makeResourceURL(file_path: string) {
|
|
|
26
27
|
|
|
27
28
|
export const load_assets_plugin = {
|
|
28
29
|
name: "sealgen-load-assets",
|
|
29
|
-
setup(build:
|
|
30
|
+
setup(build: PluginBuild) {
|
|
30
31
|
build.onLoad(
|
|
31
32
|
{
|
|
32
33
|
filter: new RegExp(
|
|
33
34
|
`\\.(${embeddable_file_extensions.join("|")})$`
|
|
34
35
|
),
|
|
35
36
|
},
|
|
36
|
-
async (args
|
|
37
|
-
|
|
37
|
+
async (args) => {
|
|
38
|
+
const contents = `export const url = "${await makeResourceURL(
|
|
38
39
|
args.path
|
|
39
40
|
)}";
|
|
40
41
|
|
|
@@ -2,11 +2,12 @@ import { promises as fs } from "fs";
|
|
|
2
2
|
import { extname } from "node:path";
|
|
3
3
|
import { embeddable_file_extensions } from "../embeddable-file-extensions.js";
|
|
4
4
|
import { removeNamedImports } from "../../remove-named-imports.js";
|
|
5
|
+
import { PluginBuild } from "esbuild";
|
|
5
6
|
|
|
6
7
|
export const rewrite_asset_imports_plugin = {
|
|
7
8
|
name: "sealgen-rewrite-asset-imports",
|
|
8
|
-
setup(build:
|
|
9
|
-
build.onLoad({ filter: /\.tsx?$/ }, async (args
|
|
9
|
+
setup(build: PluginBuild) {
|
|
10
|
+
build.onLoad({ filter: /\.tsx?$/ }, async (args) => {
|
|
10
11
|
let contents = await fs.readFile(args.path, "utf8");
|
|
11
12
|
for (const ext of embeddable_file_extensions) {
|
|
12
13
|
const regex_assets = new RegExp(
|
|
@@ -18,7 +19,7 @@ export const rewrite_asset_imports_plugin = {
|
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
contents = removeNamedImports(contents);
|
|
22
|
+
contents = removeNamedImports(contents) as string;
|
|
22
23
|
|
|
23
24
|
return {
|
|
24
25
|
contents,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import Router from "@koa/router";
|
|
2
3
|
import { hasShape, predicates } from "@sealcode/ts-predicates";
|
|
3
4
|
import { Context } from "koa";
|
|
@@ -57,7 +58,6 @@ export class StructuredArray<
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
makeSubfield(_field_name: string): FormField {
|
|
60
|
-
const fields = this;
|
|
61
61
|
return new SimpleFormField(false);
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -77,6 +77,7 @@ export class StructuredArray<
|
|
|
77
77
|
validate: false,
|
|
78
78
|
};
|
|
79
79
|
if (Array.isArray(control)) {
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
80
81
|
const [constructor, field_name, params] = control;
|
|
81
82
|
const field = this.makeSubfield(field_name);
|
|
82
83
|
if (!field) {
|
|
@@ -173,13 +174,14 @@ export class StructuredArray<
|
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
async getTargetItemID(ctx: Context): Promise<string> {
|
|
176
|
-
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
178
|
+
return ctx.params.id as string;
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
mount(router: Router) {
|
|
180
182
|
console.log("mounting!", this.getActionURL());
|
|
181
183
|
router.post(this.getActionURL(), async (ctx) => {
|
|
182
|
-
|
|
184
|
+
const form_id = ctx.query.form_id;
|
|
183
185
|
if (typeof form_id != "string") {
|
|
184
186
|
throw new Error("Missing form_id query param");
|
|
185
187
|
}
|