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: [
"./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: [
{

View File

@ -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'."

View File

@ -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);

View File

@ -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);

View File

@ -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<typeof component_arguments> {
return component_arguments;
}
toHTML(
{ pins }: ExtractStructuredComponentArgumentsValues<typeof component_arguments>,
{ render_markdown }: JDDContext
): FlatTemplatable {
toHTML({
pins,
}: ExtractStructuredComponentArgumentsValues<
typeof component_arguments
>): FlatTemplatable {
return (
<div class="map-with-pins">
<link

View File

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

View File

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

View File

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

View File

@ -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<Record<string, ComponentArgument<unknown>>>
>({
state,

View File

@ -17,26 +17,46 @@ import { jdd_context } from "./jdd-context.js";
export const actionName = "Components";
function id<X>(_: any, __: any, x: X): X {
function id<X>(_: 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<string, unknown>) {
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<unknown>,
arg_name: string,
value: unknown
) => Promise<unknown>
> = {
list: async function (
arg: List<ComponentArgument<unknown>>,
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<ComponentArgument<unknown>>).item_type;
const nested_arg_type = arg.item_type;
let array_result: Array<unknown> = 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<Record<string, ComponentArgument<unknown>>>,
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<unknown> = (
arg as Structured<Record<string, ComponentArgument<unknown>>>
).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<string, unknown> = {};
await Promise.all(
Object.entries(value).map(async ([obj_key, obj_value]) => {
const nested_arg_type: ComponentArgument<unknown> =
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<unknown> = (
arg as Structured<Record<string, ComponentArgument<unknown>>>
).structure[key];
const nested_arg_type: ComponentArgument<unknown> =
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<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,
[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<any>, arg_name: string, value: unknown) => Promise<unknown>
>;
};
export type ComponentPreviewState = {
component: string;
@ -180,7 +206,7 @@ export default new (class ComponentsPage extends StatefulPage<
async preprocessRequestBody<
T extends StateAndMetadata<ComponentPreviewState, typeof ComponentPreviewActions>
>(values: Record<string, unknown>): Promise<T> {
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<string, string>
) {
render(_ctx: BaseContext, state: ComponentPreviewState) {
const all_components = registry.getAll();
const component =
registry.get(state.component) || Object.values(all_components)[0];

View File

@ -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;
};

View File

@ -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(),

View File

@ -55,16 +55,18 @@ export default new (class SignInForm extends Form<typeof fields, void> {
}
return { canAccess: true, message: "" };
}
/* eslint-disable @typescript-eslint/no-unused-vars */
async onSuccess(
_: Context,
__: FormData<string>,
_submitResult: void
/* eslint-enable @typescript-eslint/no-unused-vars */
): Promise<FormReaction> {
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<typeof fields, void> {
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, {

View File

@ -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";

View File

@ -74,13 +74,12 @@ export default new (class SignUpForm extends Form<typeof fields, void> {
messages: [
{
type: "error",
text: `An unexpected error occurred, try again. <br> Error${
error as string
}`,
text: `An unexpected error occurred, try again. <br> Error: ${String(
error
)}`,
},
],
};
return reaction;
}
@ -126,6 +125,7 @@ export default new (class SignUpForm extends Form<typeof fields, void> {
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);

View File

@ -81,20 +81,17 @@ export default new (class TodoForm extends Form<typeof fields, void> {
} 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;
}
}

View File

@ -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<void>((resolve, reject) => {
subprocess.on("close", (code) =>
code === 0 ? resolve() : reject(new Error("Webhint tests failed"))

View File

@ -27,8 +27,11 @@ export async function withProdApp(
}) => Promise<void>
) {
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,

View File

@ -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

View File

@ -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;