Fixed most linter issues

Summary:
Ref T2734

Test Plan: eslint .

Reviewers: #testers

Subscribers: kuba-orlik, jenkins-user

Maniphest Tasks: T2734

Differential Revision: https://hub.sealcode.org/D1385
This commit is contained in:
Kuba Orlik 2024-03-21 14:39:36 +01:00
parent dc8e9eefed
commit 8e2d89ab3f
20 changed files with 124 additions and 98 deletions

View File

@ -16,16 +16,17 @@ module.exports = {
project: [ project: [
"./src/back/tsconfig.json", "./src/back/tsconfig.json",
"./src/front/tsconfig.json", "./src/front/tsconfig.json",
"./src/scripts/tsconfig.json",
], ],
}, },
rules: { rules: {
"@typescript-eslint/no-unused-vars": [2, { "varsIgnorePattern": "TempstreamJSX" }],
"@typescript-eslint/require-await": 0, "@typescript-eslint/require-await": 0,
/* "jsdoc/require-description": 2, */ /* "jsdoc/require-description": 2, */
"no-await-in-loop": 2, "no-await-in-loop": 2,
"@typescript-eslint/consistent-type-assertions": [1, { assertionStyle: "never" }], "@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" } }, settings: { jsdoc: { mode: "typescript" } },
overrides: [ overrides: [
{ {

View File

@ -22,7 +22,7 @@ describe("user-roles", () => {
session session
); );
}, },
(e: any) => { (e) => {
assert.equal( assert.equal(
e?.response.data.data.field_messages.role?.message, e?.response.data.data.field_messages.role?.message,
"Missing value for field 'role'." "Missing value for field 'role'."

View File

@ -10,7 +10,7 @@ const locreq = _locreq(module_dirname(import.meta.url));
const app = new TheApp(); const app = new TheApp();
(async function () { void (async function () {
await kill(PORT); await kill(PORT);
await kill(PORT); await kill(PORT);

View File

@ -3,11 +3,11 @@ import { Registry } from "@sealcode/jdd";
export const registry = new Registry(); 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"; import { MapWithPins } from "./map-with-pins/map-with-pins.jdd.js";
registry.add("map-with-pins", MapWithPins); 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"; import { NiceBox } from "./nice-box/nice-box.jdd.js";
registry.add("nice-box", NiceBox); registry.add("nice-box", NiceBox);

View File

@ -3,7 +3,6 @@ import {
Component, Component,
ComponentArguments, ComponentArguments,
ExtractStructuredComponentArgumentsValues, ExtractStructuredComponentArgumentsValues,
JDDContext,
} from "@sealcode/jdd"; } from "@sealcode/jdd";
const coordinates = new ComponentArguments.ShortText(); const coordinates = new ComponentArguments.ShortText();
@ -33,10 +32,11 @@ export class MapWithPins extends Component<typeof component_arguments> {
return component_arguments; return component_arguments;
} }
toHTML( toHTML({
{ pins }: ExtractStructuredComponentArgumentsValues<typeof component_arguments>, pins,
{ render_markdown }: JDDContext }: ExtractStructuredComponentArgumentsValues<
): FlatTemplatable { typeof component_arguments
>): FlatTemplatable {
return ( return (
<div class="map-with-pins"> <div class="map-with-pins">
<link <link

View File

@ -1,4 +1,4 @@
import { FlatTemplatable, TempstreamJSX } from "tempstream"; import { TempstreamJSX } from "tempstream";
import { import {
Component, Component,
ComponentArguments, ComponentArguments,
@ -29,7 +29,7 @@ export class NiceBox extends Component<typeof component_arguments> {
content, content,
images, images,
}: ExtractStructuredComponentArgumentsValues<typeof component_arguments>, }: ExtractStructuredComponentArgumentsValues<typeof component_arguments>,
{ render_markdown, decode_file, render_image }: JDDContext { render_markdown, render_image }: JDDContext
): Promise<Readable> { ): Promise<Readable> {
return ( return (
<div class="nice-box"> <div class="nice-box">

View File

@ -4,6 +4,8 @@ import frame from "../../frame.js";
import { Tasks } from "../../collections/collections.js"; import { Tasks } from "../../collections/collections.js";
export function Task(task: CollectionItem<typeof Tasks>) { export function Task(task: CollectionItem<typeof Tasks>) {
const title = task.get("title");
const title_string = title ? title : "";
return frame( return frame(
`task-${task.id}`, `task-${task.id}`,
/* HTML */ `<li class="task"> /* HTML */ `<li class="task">
@ -14,7 +16,7 @@ export function Task(task: CollectionItem<typeof Tasks>) {
data-id="${task.id}" data-id="${task.id}"
${task.get("done") ? "checked" : ""} ${task.get("done") ? "checked" : ""}
/> />
${task.get("title") as string} ${title_string}
<form method="POST" action="/todo/"> <form method="POST" action="/todo/">
<input class="delete-button" type="submit" value="Delete" /> <input class="delete-button" type="submit" value="Delete" />
<input <input

View File

@ -2,8 +2,7 @@ import { Enum } from "@sealcode/jdd";
import { TempstreamJSX } from "tempstream"; import { TempstreamJSX } from "tempstream";
import { printArgPath } from "./print-arg-path.js"; import { printArgPath } from "./print-arg-path.js";
export function ComponentInputEnum<State, T extends Enum<any>>({ export function ComponentInputEnum<State, S extends string, T extends Enum<S>>({
state,
arg_path, arg_path,
arg, arg,
value, value,
@ -23,7 +22,7 @@ export function ComponentInputEnum<State, T extends Enum<any>>({
name={`$.component_args${printArgPath(arg_path)}`} name={`$.component_args${printArgPath(arg_path)}`}
onchange={onchange} onchange={onchange}
> >
{arg.values.map((v) => ( {arg.values.map((v: S) => (
<option value={v} selected={value == v}> <option value={v} selected={value == v}>
{v} {v}
</option> </option>

View File

@ -4,10 +4,8 @@ import { TempstreamJSX } from "tempstream";
import { ComponentPreviewState } from "../components.sreact.js"; import { ComponentPreviewState } from "../components.sreact.js";
import { ComponentInput } from "./component-input.js"; import { ComponentInput } from "./component-input.js";
import { ComponentPreviewActions } from "./component-preview-actions.js"; import { ComponentPreviewActions } from "./component-preview-actions.js";
import { printArgPath } from "./print-arg-path.js";
export function ComponentInputStructured< export function ComponentInputStructured<
State,
T extends Structured<Record<string, ComponentArgument<unknown>>> T extends Structured<Record<string, ComponentArgument<unknown>>>
>({ >({
state, state,

View File

@ -17,26 +17,46 @@ import { jdd_context } from "./jdd-context.js";
export const actionName = "Components"; export const actionName = "Components";
function id<X>(_: any, __: any, x: X): X { function id<X>(_: unknown, __: unknown, x: X): X {
return 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<string, unknown>) { async function encodeSealiousFile(maybe_file: Record<string, unknown>) {
if (maybe_file?.getDataPath) { if (isSealiousFile(maybe_file)) {
return simpleJDDContext.encode_file( return simpleJDDContext.encode_file(
{ {
type: "path", type: "path",
// asserting that this is an instance of sealious' FileFromPath // asserting that this is an instance of sealious' FileFromPath
path: (maybe_file as unknown as { data: { path: string } }).data path: maybe_file.data.path,
.path as string,
}, },
false false
); );
} }
} }
const componentArgToRequestProcessor = { const componentArgToRequestProcessor: Record<
list: async function (arg, arg_name, value: unknown) { string,
(
arg: ComponentArgument<unknown>,
arg_name: string,
value: unknown
) => Promise<unknown>
> = {
list: async function (
arg: List<ComponentArgument<unknown>>,
arg_name,
value: unknown
) {
if ( if (
!is(value, predicates.array(predicates.object)) && !is(value, predicates.array(predicates.object)) &&
!is(value, predicates.object) !is(value, predicates.object)
@ -44,12 +64,13 @@ const componentArgToRequestProcessor = {
throw new Error(`$.${arg_name} is not a list or object`); throw new Error(`$.${arg_name} is not a list or object`);
} }
const values = Array.isArray(value) ? value : Object.values(value); const values = Array.isArray(value) ? value : Object.values(value);
const nested_arg_type = (arg as List<ComponentArgument<unknown>>).item_type; const nested_arg_type = arg.item_type;
let array_result: Array<unknown> = await Promise.all( let array_result: Array<unknown> = await Promise.all(
values.map((value, index) => { values.map(async (value, index) => {
return ( const result = await (
componentArgToRequestProcessor[nested_arg_type.getTypeName()] || id componentArgToRequestProcessor[nested_arg_type.getTypeName()] || id
)(nested_arg_type, `${arg_name}[${index}]`, value); )(nested_arg_type, `${arg_name}[${index}]`, value);
return result;
}) })
); );
if (nested_arg_type.getTypeName() != "list") { if (nested_arg_type.getTypeName() != "list") {
@ -57,35 +78,35 @@ const componentArgToRequestProcessor = {
} }
return array_result; return array_result;
}, },
structured: async function (arg, arg_name, value) { structured: async function (
arg: Structured<Record<string, ComponentArgument<unknown>>>,
arg_name,
value
) {
if (!is(value, predicates.object)) { if (!is(value, predicates.object)) {
throw new Error(`${arg_name} is not an object`); throw new Error(`${arg_name} is not an object`);
} }
let result = Object.fromEntries( const result: Record<string, unknown> = {};
await Promise.all( await Promise.all(
Object.entries(value).map(async ([obj_key, obj_value]) => { Object.entries(value).map(async ([obj_key, obj_value]) => {
const nested_arg_type: ComponentArgument<unknown> = ( const nested_arg_type: ComponentArgument<unknown> =
arg as Structured<Record<string, ComponentArgument<unknown>>> arg.structure[obj_key];
).structure[obj_key];
if (!nested_arg_type) { if (!nested_arg_type) {
return [obj_key, null]; return [obj_key, null];
} }
const new_value = await ( const new_value = await (
componentArgToRequestProcessor[nested_arg_type.getTypeName()] || componentArgToRequestProcessor[nested_arg_type.getTypeName()] || id
id
)(arg, `${arg_name}[${obj_key}]`, obj_value); )(arg, `${arg_name}[${obj_key}]`, obj_value);
return [obj_key, new_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 we're in a list and any of the values return an array, we will multiply the object
if (arg.hasParent("list")) { if (arg.hasParent("list")) {
const keys_with_unexpected_arrays = Object.entries(result) const keys_with_unexpected_arrays = Object.entries(result)
.filter(([key, value]) => { .filter(([key, value]) => {
const nested_arg_type: ComponentArgument<unknown> = ( const nested_arg_type: ComponentArgument<unknown> =
arg as Structured<Record<string, ComponentArgument<unknown>>> arg.structure[key];
).structure[key];
return ( return (
nested_arg_type.getTypeName() !== "list" && Array.isArray(value) nested_arg_type.getTypeName() !== "list" && Array.isArray(value)
); );
@ -100,13 +121,21 @@ const componentArgToRequestProcessor = {
if (keys_with_unexpected_arrays.length == 1) { if (keys_with_unexpected_arrays.length == 1) {
const key = keys_with_unexpected_arrays[0]; const key = keys_with_unexpected_arrays[0];
const old_result = result; const old_result = result;
result = (old_result[key] as Array<unknown>).map((value) => ({ const array = old_result[key];
if (!Array.isArray(array)) {
throw new Error("expected an array");
}
return array.map((value: unknown) => ({
...old_result, ...old_result,
[key]: value, [key]: value,
})); }));
} } else {
}
return result; return result;
}
} else {
return result;
}
}, },
image: async function (arg, _, value: unknown) { image: async function (arg, _, value: unknown) {
if ( if (
@ -129,10 +158,7 @@ const componentArgToRequestProcessor = {
return Promise.all(files.map(encodeSealiousFile)); return Promise.all(files.map(encodeSealiousFile));
} }
}, },
} as Record< };
string,
(arg: ComponentArgument<any>, arg_name: string, value: unknown) => Promise<unknown>
>;
export type ComponentPreviewState = { export type ComponentPreviewState = {
component: string; component: string;
@ -180,7 +206,7 @@ export default new (class ComponentsPage extends StatefulPage<
async preprocessRequestBody< async preprocessRequestBody<
T extends StateAndMetadata<ComponentPreviewState, typeof ComponentPreviewActions> T extends StateAndMetadata<ComponentPreviewState, typeof ComponentPreviewActions>
>(values: Record<string, unknown>): Promise<T> { >(values: Record<string, unknown>): Promise<T> {
let old_component = hasFieldOfType(values, "component", predicates.string) const old_component = hasFieldOfType(values, "component", predicates.string)
? values.component ? values.component
: null; : null;
@ -206,10 +232,12 @@ export default new (class ComponentsPage extends StatefulPage<
) )
) { ) {
// no component args to overwrite // no component args to overwrite
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return values as T; return values as T;
} }
for (const [arg_name, arg] of Object.entries(component.getArguments())) { const promises = Object.entries(component.getArguments()).map(
let value = values.$.component_args[arg_name]; async ([arg_name, arg]) => {
const value = values.$.component_args[arg_name];
if (value) { if (value) {
const new_value = await ( const new_value = await (
componentArgToRequestProcessor[arg.getTypeName()] || id componentArgToRequestProcessor[arg.getTypeName()] || id
@ -217,14 +245,13 @@ export default new (class ComponentsPage extends StatefulPage<
values.$.component_args[arg_name] = new_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; return values as T;
} }
render( render(_ctx: BaseContext, state: ComponentPreviewState) {
ctx: BaseContext,
state: ComponentPreviewState,
inputs: Record<string, string>
) {
const all_components = registry.getAll(); const all_components = registry.getAll();
const component = const component =
registry.get(state.component) || Object.values(all_components)[0]; registry.get(state.component) || Object.values(all_components)[0];

View File

@ -1,7 +1,7 @@
import { simpleJDDContext, JDDContext } from "@sealcode/jdd"; import { simpleJDDContext, JDDContext } from "@sealcode/jdd";
import { imageRouter } from "../image-router.js"; import { imageRouter } from "../image-router.js";
export const jdd_context = { export const jdd_context: JDDContext = {
...simpleJDDContext, ...simpleJDDContext,
render_image: async (image_id, args) => { render_image: async (image_id, args) => {
if (!image_id) { if (!image_id) {
@ -13,4 +13,4 @@ export const jdd_context = {
} }
return imageRouter.image(image_pointer.path, args); return imageRouter.image(image_pointer.path, args);
}, },
} as JDDContext; };

View File

@ -13,7 +13,7 @@ export default new (class LogoutRedirect extends Mountable {
mount(router: Router, path: string) { mount(router: Router, path: string) {
router.get(path, async (ctx) => { router.get(path, async (ctx) => {
try { try {
const session_id: string = ctx.cookies.get("sealious-session") as string; const session_id = ctx.cookies.get("sealious-session");
if (session_id) { if (session_id) {
await ctx.$app.collections.sessions.logout( await ctx.$app.collections.sessions.logout(
new ctx.$app.SuperContext(), new ctx.$app.SuperContext(),

View File

@ -55,16 +55,18 @@ export default new (class SignInForm extends Form<typeof fields, void> {
} }
return { canAccess: true, message: "" }; return { canAccess: true, message: "" };
} }
/* eslint-disable @typescript-eslint/no-unused-vars */
async onSuccess( async onSuccess(
_: Context, _: Context,
__: FormData<string>, __: FormData<string>,
_submitResult: void _submitResult: void
/* eslint-enable @typescript-eslint/no-unused-vars */
): Promise<FormReaction> { ): Promise<FormReaction> {
const reaction: FormReaction = { const reaction: FormReaction = {
action: "redirect", action: "redirect",
url: "/", url: "/",
}; };
// eslint-disable-next-line no-console
console.log("Successfully logged in."); console.log("Successfully logged in.");
return reaction; return reaction;
} }
@ -90,8 +92,8 @@ export default new (class SignInForm extends Form<typeof fields, void> {
async onSubmit(ctx: Context, data: FormData) { async onSubmit(ctx: Context, data: FormData) {
try { try {
const sessionId: string = await Users.app.collections.sessions.login( const sessionId: string = await Users.app.collections.sessions.login(
data.raw_values.username as string, String(data.raw_values.username),
data.raw_values.password as string String(data.raw_values.password)
); );
ctx.cookies.set("sealious-session", sessionId, { ctx.cookies.set("sealious-session", sessionId, {

View File

@ -1,6 +1,5 @@
import { Browser, BrowserContext, Page } from "@playwright/test";
import ADMIN_CREDENTIALS from "../default-admin-credentials.js"; 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 { VERY_LONG_TEST_TIMEOUT, webhintURL } from "../test_utils/webhint.js";
import { withProdApp } from "../test_utils/with-prod-app.js"; import { withProdApp } from "../test_utils/with-prod-app.js";
import { LogoutURL, SignInURL } from "./urls.js"; import { LogoutURL, SignInURL } from "./urls.js";

View File

@ -74,13 +74,12 @@ export default new (class SignUpForm extends Form<typeof fields, void> {
messages: [ messages: [
{ {
type: "error", type: "error",
text: `An unexpected error occurred, try again. <br> Error${ text: `An unexpected error occurred, try again. <br> Error: ${String(
error as string error
}`, )}`,
}, },
], ],
}; };
return reaction; return reaction;
} }
@ -126,6 +125,7 @@ export default new (class SignUpForm extends Form<typeof fields, void> {
email: email, email: email,
roles: [], roles: [],
}); });
// eslint-disable-next-line no-console
console.log("A user was created successfully."); console.log("A user was created successfully.");
} catch (error) { } catch (error) {
console.error("Error during user creation:", error); console.error("Error during user creation:", error);

View File

@ -81,20 +81,17 @@ export default new (class TodoForm extends Form<typeof fields, void> {
} catch (error) { } catch (error) {
throw new Error(); throw new Error();
} }
console.debug(`task has been successfully created`);
break; break;
} }
case "delete": { case "delete": {
const task = await ctx.$app.collections.tasks.getByID( const task = await ctx.$app.collections.tasks.getByID(
ctx.$context, ctx.$context,
data.raw_values.taskId as string String(data.raw_values.taskId)
); );
await task.remove(ctx.$context); await task.remove(ctx.$context);
console.debug(`task has been successfully removed`);
break; break;
} }
default: { default: {
console.debug("Wrong action");
break; break;
} }
} }

View File

@ -11,14 +11,14 @@ export async function webhintURL(url: string, config = locreq.resolve(".hintrc")
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log("scanning with webhint....", url); console.log("scanning with webhint....", url);
try { try {
console.log("cwd", locreq.resolve("webhint"));
const subprocess = spawn( const subprocess = spawn(
"node", "node",
[locreq.resolve("webhint/node_modules/.bin/hint"), "--config", config, url], [locreq.resolve("webhint/node_modules/.bin/hint"), "--config", config, url],
{ cwd: locreq.resolve("webhint") } // to prevent webhint from trying to parese source code { cwd: locreq.resolve("webhint") } // to prevent webhint from trying to parese source code
); );
subprocess.stderr.on("data", (b) => console.error(b.toString())); subprocess.stderr.on("data", (b) => console.error(String(b)));
subprocess.stdout.on("data", (b) => console.log(b.toString())); // eslint-disable-next-line no-console
subprocess.stdout.on("data", (b) => console.log(String(b)));
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
subprocess.on("close", (code) => subprocess.on("close", (code) =>
code === 0 ? resolve() : reject(new Error("Webhint tests failed")) code === 0 ? resolve() : reject(new Error("Webhint tests failed"))

View File

@ -27,8 +27,11 @@ export async function withProdApp(
}) => Promise<void> }) => Promise<void>
) { ) {
const app = new TheApp(); 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["www-server"].port = port;
app.config.datastore_mongo = { app.config.datastore_mongo = {
...app.config.datastore_mongo, ...app.config.datastore_mongo,

View File

@ -3,14 +3,12 @@ import { Controller } from "stimulus";
export default class InputImagePreview extends Controller { export default class InputImagePreview extends Controller {
id: string; id: string;
handleChange(event) { handleChange() {
const img = this.element.querySelector("img"); const img = this.element.querySelector("img");
console.log({ img });
window.URL.revokeObjectURL(img.src); window.URL.revokeObjectURL(img.src);
const new_url = window.URL.createObjectURL( const new_url = window.URL.createObjectURL(
this.element.querySelector("input").files[0] this.element.querySelector("input").files[0]
); );
console.log({ new_url });
img.src = new_url; img.src = new_url;
img.parentNode; img.parentNode;
img.parentElement img.parentElement

View File

@ -11,7 +11,7 @@ export default class TaskController extends Controller {
} }
async toggle(event: Event) { async toggle(event: Event) {
const inputElement: HTMLInputElement = event.target as HTMLInputElement; const inputElement = event.target;
if (inputElement instanceof HTMLInputElement) { if (inputElement instanceof HTMLInputElement) {
const isChecked: boolean = inputElement.checked; const isChecked: boolean = inputElement.checked;