diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 5a2f1d4..01ae52a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -16,16 +16,17 @@ module.exports = { project: [ "./src/back/tsconfig.json", "./src/front/tsconfig.json", - "./src/scripts/tsconfig.json", ], }, rules: { + "@typescript-eslint/no-unused-vars": [2, { "varsIgnorePattern": "TempstreamJSX" }], "@typescript-eslint/require-await": 0, /* "jsdoc/require-description": 2, */ "no-await-in-loop": 2, "@typescript-eslint/consistent-type-assertions": [1, { assertionStyle: "never" }], - "no-console": 1, + "no-console": [1, { "allow": ["error"] }] }, + "ignorePatterns": ["dist/*", "public/dist/*", "coverage/*", "webhint/*"], settings: { jsdoc: { mode: "typescript" } }, overrides: [ { diff --git a/src/back/collections/user-roles.test.ts b/src/back/collections/user-roles.test.ts index 42601a9..0a969c0 100644 --- a/src/back/collections/user-roles.test.ts +++ b/src/back/collections/user-roles.test.ts @@ -22,7 +22,7 @@ describe("user-roles", () => { session ); }, - (e: any) => { + (e) => { assert.equal( e?.response.data.data.field_messages.role?.message, "Missing value for field 'role'." diff --git a/src/back/index.ts b/src/back/index.ts index 1687cbd..62153f7 100644 --- a/src/back/index.ts +++ b/src/back/index.ts @@ -10,7 +10,7 @@ const locreq = _locreq(module_dirname(import.meta.url)); const app = new TheApp(); -(async function () { +void (async function () { await kill(PORT); await kill(PORT); diff --git a/src/back/jdd-components/components.ts b/src/back/jdd-components/components.ts index 35cee56..23af942 100644 --- a/src/back/jdd-components/components.ts +++ b/src/back/jdd-components/components.ts @@ -3,11 +3,11 @@ import { Registry } from "@sealcode/jdd"; export const registry = new Registry(); -import { ImageDemo } from "./image-demo/image-demo.jdd.js"; -registry.add("image-demo", ImageDemo); - import { MapWithPins } from "./map-with-pins/map-with-pins.jdd.js"; registry.add("map-with-pins", MapWithPins); +import { ImageDemo } from "./image-demo/image-demo.jdd.js"; +registry.add("image-demo", ImageDemo); + import { NiceBox } from "./nice-box/nice-box.jdd.js"; registry.add("nice-box", NiceBox); diff --git a/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx b/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx index 27bac7b..83c0b48 100644 --- a/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx +++ b/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx @@ -3,7 +3,6 @@ import { Component, ComponentArguments, ExtractStructuredComponentArgumentsValues, - JDDContext, } from "@sealcode/jdd"; const coordinates = new ComponentArguments.ShortText(); @@ -33,10 +32,11 @@ export class MapWithPins extends Component { return component_arguments; } - toHTML( - { pins }: ExtractStructuredComponentArgumentsValues, - { render_markdown }: JDDContext - ): FlatTemplatable { + toHTML({ + pins, + }: ExtractStructuredComponentArgumentsValues< + typeof component_arguments + >): FlatTemplatable { return (
{ content, images, }: ExtractStructuredComponentArgumentsValues, - { render_markdown, decode_file, render_image }: JDDContext + { render_markdown, render_image }: JDDContext ): Promise { return (
diff --git a/src/back/routes/common/tasks-view.ts b/src/back/routes/common/tasks-view.ts index 5d74f47..f793a31 100644 --- a/src/back/routes/common/tasks-view.ts +++ b/src/back/routes/common/tasks-view.ts @@ -4,6 +4,8 @@ import frame from "../../frame.js"; import { Tasks } from "../../collections/collections.js"; export function Task(task: CollectionItem) { + const title = task.get("title"); + const title_string = title ? title : ""; return frame( `task-${task.id}`, /* HTML */ `
  • @@ -14,7 +16,7 @@ export function Task(task: CollectionItem) { data-id="${task.id}" ${task.get("done") ? "checked" : ""} /> - ${task.get("title") as string} + ${title_string}
    - ${tasksTemplate} - - `; +
      + ${tasksTemplate} +
    + `; } diff --git a/src/back/routes/component-preview/component-input-enum.tsx b/src/back/routes/component-preview/component-input-enum.tsx index 5d8784d..dbb3596 100644 --- a/src/back/routes/component-preview/component-input-enum.tsx +++ b/src/back/routes/component-preview/component-input-enum.tsx @@ -2,8 +2,7 @@ import { Enum } from "@sealcode/jdd"; import { TempstreamJSX } from "tempstream"; import { printArgPath } from "./print-arg-path.js"; -export function ComponentInputEnum>({ - state, +export function ComponentInputEnum>({ arg_path, arg, value, @@ -23,7 +22,7 @@ export function ComponentInputEnum>({ name={`$.component_args${printArgPath(arg_path)}`} onchange={onchange} > - {arg.values.map((v) => ( + {arg.values.map((v: S) => ( diff --git a/src/back/routes/component-preview/component-input-structured.tsx b/src/back/routes/component-preview/component-input-structured.tsx index efcc453..a360ba9 100644 --- a/src/back/routes/component-preview/component-input-structured.tsx +++ b/src/back/routes/component-preview/component-input-structured.tsx @@ -4,10 +4,8 @@ import { TempstreamJSX } from "tempstream"; import { ComponentPreviewState } from "../components.sreact.js"; import { ComponentInput } from "./component-input.js"; import { ComponentPreviewActions } from "./component-preview-actions.js"; -import { printArgPath } from "./print-arg-path.js"; export function ComponentInputStructured< - State, T extends Structured>> >({ state, diff --git a/src/back/routes/components.sreact.tsx b/src/back/routes/components.sreact.tsx index 6c97f6b..6a089de 100644 --- a/src/back/routes/components.sreact.tsx +++ b/src/back/routes/components.sreact.tsx @@ -17,26 +17,46 @@ import { jdd_context } from "./jdd-context.js"; export const actionName = "Components"; -function id(_: any, __: any, x: X): X { +function id(_: unknown, __: unknown, x: X): X { return x; } +function isSealiousFile(x: unknown): x is { data: { path: string } } { + return hasShape( + { + getDataPath: predicates.any, + data: predicates.shape({ path: predicates.string }), + }, + x + ); +} + async function encodeSealiousFile(maybe_file: Record) { - if (maybe_file?.getDataPath) { + if (isSealiousFile(maybe_file)) { return simpleJDDContext.encode_file( { type: "path", // asserting that this is an instance of sealious' FileFromPath - path: (maybe_file as unknown as { data: { path: string } }).data - .path as string, + path: maybe_file.data.path, }, false ); } } -const componentArgToRequestProcessor = { - list: async function (arg, arg_name, value: unknown) { +const componentArgToRequestProcessor: Record< + string, + ( + arg: ComponentArgument, + arg_name: string, + value: unknown + ) => Promise +> = { + list: async function ( + arg: List>, + arg_name, + value: unknown + ) { if ( !is(value, predicates.array(predicates.object)) && !is(value, predicates.object) @@ -44,12 +64,13 @@ const componentArgToRequestProcessor = { throw new Error(`$.${arg_name} is not a list or object`); } const values = Array.isArray(value) ? value : Object.values(value); - const nested_arg_type = (arg as List>).item_type; + const nested_arg_type = arg.item_type; let array_result: Array = await Promise.all( - values.map((value, index) => { - return ( + values.map(async (value, index) => { + const result = await ( componentArgToRequestProcessor[nested_arg_type.getTypeName()] || id )(nested_arg_type, `${arg_name}[${index}]`, value); + return result; }) ); if (nested_arg_type.getTypeName() != "list") { @@ -57,35 +78,35 @@ const componentArgToRequestProcessor = { } return array_result; }, - structured: async function (arg, arg_name, value) { + structured: async function ( + arg: Structured>>, + arg_name, + value + ) { if (!is(value, predicates.object)) { throw new Error(`${arg_name} is not an object`); } - let result = Object.fromEntries( - await Promise.all( - Object.entries(value).map(async ([obj_key, obj_value]) => { - const nested_arg_type: ComponentArgument = ( - arg as Structured>> - ).structure[obj_key]; - if (!nested_arg_type) { - return [obj_key, null]; - } - const new_value = await ( - componentArgToRequestProcessor[nested_arg_type.getTypeName()] || - id - )(arg, `${arg_name}[${obj_key}]`, obj_value); - return [obj_key, new_value]; - }) - ) + const result: Record = {}; + await Promise.all( + Object.entries(value).map(async ([obj_key, obj_value]) => { + const nested_arg_type: ComponentArgument = + arg.structure[obj_key]; + if (!nested_arg_type) { + return [obj_key, null]; + } + const new_value = await ( + componentArgToRequestProcessor[nested_arg_type.getTypeName()] || id + )(arg, `${arg_name}[${obj_key}]`, obj_value); + result[obj_key] = new_value; + }) ); // if we're in a list and any of the values return an array, we will multiply the object if (arg.hasParent("list")) { const keys_with_unexpected_arrays = Object.entries(result) .filter(([key, value]) => { - const nested_arg_type: ComponentArgument = ( - arg as Structured>> - ).structure[key]; + const nested_arg_type: ComponentArgument = + arg.structure[key]; return ( nested_arg_type.getTypeName() !== "list" && Array.isArray(value) ); @@ -100,13 +121,21 @@ const componentArgToRequestProcessor = { if (keys_with_unexpected_arrays.length == 1) { const key = keys_with_unexpected_arrays[0]; const old_result = result; - result = (old_result[key] as Array).map((value) => ({ + const array = old_result[key]; + if (!Array.isArray(array)) { + throw new Error("expected an array"); + } + + return array.map((value: unknown) => ({ ...old_result, [key]: value, })); + } else { + return result; } + } else { + return result; } - return result; }, image: async function (arg, _, value: unknown) { if ( @@ -129,10 +158,7 @@ const componentArgToRequestProcessor = { return Promise.all(files.map(encodeSealiousFile)); } }, -} as Record< - string, - (arg: ComponentArgument, arg_name: string, value: unknown) => Promise ->; +}; export type ComponentPreviewState = { component: string; @@ -180,7 +206,7 @@ export default new (class ComponentsPage extends StatefulPage< async preprocessRequestBody< T extends StateAndMetadata >(values: Record): Promise { - let old_component = hasFieldOfType(values, "component", predicates.string) + const old_component = hasFieldOfType(values, "component", predicates.string) ? values.component : null; @@ -206,25 +232,26 @@ export default new (class ComponentsPage extends StatefulPage< ) ) { // no component args to overwrite + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return values as T; } - for (const [arg_name, arg] of Object.entries(component.getArguments())) { - let value = values.$.component_args[arg_name]; - if (value) { - const new_value = await ( - componentArgToRequestProcessor[arg.getTypeName()] || id - )(arg, arg_name, value); - values.$.component_args[arg_name] = new_value; + const promises = Object.entries(component.getArguments()).map( + async ([arg_name, arg]) => { + const value = values.$.component_args[arg_name]; + if (value) { + const new_value = await ( + componentArgToRequestProcessor[arg.getTypeName()] || id + )(arg, arg_name, value); + values.$.component_args[arg_name] = new_value; + } } - } + ); + await Promise.all(promises); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return values as T; } - render( - ctx: BaseContext, - state: ComponentPreviewState, - inputs: Record - ) { + render(_ctx: BaseContext, state: ComponentPreviewState) { const all_components = registry.getAll(); const component = registry.get(state.component) || Object.values(all_components)[0]; diff --git a/src/back/routes/jdd-context.ts b/src/back/routes/jdd-context.ts index a5f5cf7..65045e9 100644 --- a/src/back/routes/jdd-context.ts +++ b/src/back/routes/jdd-context.ts @@ -1,7 +1,7 @@ import { simpleJDDContext, JDDContext } from "@sealcode/jdd"; import { imageRouter } from "../image-router.js"; -export const jdd_context = { +export const jdd_context: JDDContext = { ...simpleJDDContext, render_image: async (image_id, args) => { if (!image_id) { @@ -13,4 +13,4 @@ export const jdd_context = { } return imageRouter.image(image_pointer.path, args); }, -} as JDDContext; +}; diff --git a/src/back/routes/logout.redirect.ts b/src/back/routes/logout.redirect.ts index 8c59d2a..aa55837 100644 --- a/src/back/routes/logout.redirect.ts +++ b/src/back/routes/logout.redirect.ts @@ -13,7 +13,7 @@ export default new (class LogoutRedirect extends Mountable { mount(router: Router, path: string) { router.get(path, async (ctx) => { try { - const session_id: string = ctx.cookies.get("sealious-session") as string; + const session_id = ctx.cookies.get("sealious-session"); if (session_id) { await ctx.$app.collections.sessions.logout( new ctx.$app.SuperContext(), diff --git a/src/back/routes/signIn.form.ts b/src/back/routes/signIn.form.ts index d59af9e..6bb3a88 100644 --- a/src/back/routes/signIn.form.ts +++ b/src/back/routes/signIn.form.ts @@ -55,16 +55,18 @@ export default new (class SignInForm extends Form { } return { canAccess: true, message: "" }; } - + /* eslint-disable @typescript-eslint/no-unused-vars */ async onSuccess( _: Context, __: FormData, _submitResult: void + /* eslint-enable @typescript-eslint/no-unused-vars */ ): Promise { const reaction: FormReaction = { action: "redirect", url: "/", }; + // eslint-disable-next-line no-console console.log("Successfully logged in."); return reaction; } @@ -90,8 +92,8 @@ export default new (class SignInForm extends Form { async onSubmit(ctx: Context, data: FormData) { try { const sessionId: string = await Users.app.collections.sessions.login( - data.raw_values.username as string, - data.raw_values.password as string + String(data.raw_values.username), + String(data.raw_values.password) ); ctx.cookies.set("sealious-session", sessionId, { diff --git a/src/back/routes/signIn.test.ts b/src/back/routes/signIn.test.ts index 32bd739..353f2b0 100644 --- a/src/back/routes/signIn.test.ts +++ b/src/back/routes/signIn.test.ts @@ -1,6 +1,5 @@ -import { Browser, BrowserContext, Page } from "@playwright/test"; import ADMIN_CREDENTIALS from "../default-admin-credentials.js"; -import { getBrowser, getPage } from "../test_utils/browser-creator.js"; +import { getPage } from "../test_utils/browser-creator.js"; import { VERY_LONG_TEST_TIMEOUT, webhintURL } from "../test_utils/webhint.js"; import { withProdApp } from "../test_utils/with-prod-app.js"; import { LogoutURL, SignInURL } from "./urls.js"; diff --git a/src/back/routes/signUp.form.ts b/src/back/routes/signUp.form.ts index a184146..a5a5daf 100644 --- a/src/back/routes/signUp.form.ts +++ b/src/back/routes/signUp.form.ts @@ -74,13 +74,12 @@ export default new (class SignUpForm extends Form { messages: [ { type: "error", - text: `An unexpected error occurred, try again.
    Error${ - error as string - }`, + text: `An unexpected error occurred, try again.
    Error: ${String( + error + )}`, }, ], }; - return reaction; } @@ -126,6 +125,7 @@ export default new (class SignUpForm extends Form { email: email, roles: [], }); + // eslint-disable-next-line no-console console.log("A user was created successfully."); } catch (error) { console.error("Error during user creation:", error); diff --git a/src/back/routes/todo.form.ts b/src/back/routes/todo.form.ts index 73e0067..01d819f 100644 --- a/src/back/routes/todo.form.ts +++ b/src/back/routes/todo.form.ts @@ -81,20 +81,17 @@ export default new (class TodoForm extends Form { } catch (error) { throw new Error(); } - console.debug(`task has been successfully created`); break; } case "delete": { const task = await ctx.$app.collections.tasks.getByID( ctx.$context, - data.raw_values.taskId as string + String(data.raw_values.taskId) ); await task.remove(ctx.$context); - console.debug(`task has been successfully removed`); break; } default: { - console.debug("Wrong action"); break; } } diff --git a/src/back/test_utils/webhint.ts b/src/back/test_utils/webhint.ts index 1af364f..663c059 100644 --- a/src/back/test_utils/webhint.ts +++ b/src/back/test_utils/webhint.ts @@ -11,14 +11,14 @@ export async function webhintURL(url: string, config = locreq.resolve(".hintrc") // eslint-disable-next-line no-console console.log("scanning with webhint....", url); try { - console.log("cwd", locreq.resolve("webhint")); const subprocess = spawn( "node", [locreq.resolve("webhint/node_modules/.bin/hint"), "--config", config, url], { cwd: locreq.resolve("webhint") } // to prevent webhint from trying to parese source code ); - subprocess.stderr.on("data", (b) => console.error(b.toString())); - subprocess.stdout.on("data", (b) => console.log(b.toString())); + subprocess.stderr.on("data", (b) => console.error(String(b))); + // eslint-disable-next-line no-console + subprocess.stdout.on("data", (b) => console.log(String(b))); await new Promise((resolve, reject) => { subprocess.on("close", (code) => code === 0 ? resolve() : reject(new Error("Webhint tests failed")) diff --git a/src/back/test_utils/with-prod-app.ts b/src/back/test_utils/with-prod-app.ts index ba431ba..54d3f65 100644 --- a/src/back/test_utils/with-prod-app.ts +++ b/src/back/test_utils/with-prod-app.ts @@ -27,8 +27,11 @@ export async function withProdApp( }) => Promise ) { const app = new TheApp(); - const port = (await port_numbers().next()).value as number; - + const port = (await port_numbers().next()).value; + if (!port) { + console.error("Constant port is empty."); + return; + } app.config["www-server"].port = port; app.config.datastore_mongo = { ...app.config.datastore_mongo, diff --git a/src/front/controllers/input-image-preview.ts b/src/front/controllers/input-image-preview.ts index d3a654d..1820bf5 100644 --- a/src/front/controllers/input-image-preview.ts +++ b/src/front/controllers/input-image-preview.ts @@ -3,14 +3,12 @@ import { Controller } from "stimulus"; export default class InputImagePreview extends Controller { id: string; - handleChange(event) { + handleChange() { const img = this.element.querySelector("img"); - console.log({ img }); window.URL.revokeObjectURL(img.src); const new_url = window.URL.createObjectURL( this.element.querySelector("input").files[0] ); - console.log({ new_url }); img.src = new_url; img.parentNode; img.parentElement diff --git a/src/front/controllers/task-controller.ts b/src/front/controllers/task-controller.ts index ed57c78..e48dbf7 100644 --- a/src/front/controllers/task-controller.ts +++ b/src/front/controllers/task-controller.ts @@ -11,7 +11,7 @@ export default class TaskController extends Controller { } async toggle(event: Event) { - const inputElement: HTMLInputElement = event.target as HTMLInputElement; + const inputElement = event.target; if (inputElement instanceof HTMLInputElement) { const isChecked: boolean = inputElement.checked;