Skip to content

Commit

Permalink
Merge pull request #15 from iamgiolaga/refactor/log-rework
Browse files Browse the repository at this point in the history
refactor: rework of logs
  • Loading branch information
allevo committed May 20, 2024
2 parents 0069d92 + 0234ab6 commit f551486
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 99 deletions.
14 changes: 11 additions & 3 deletions examples/custom-element/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ class CounterElement extends HTMLElement {
connectedCallback() {
const div = document.createElement("div");
if (shadowDomMode) {
const shadow = this.attachShadow({ mode: shadowDomMode });
const shadow = this.attachShadow({
mode: shadowDomMode,
});
if (typeof injectCSS === "function") {
injectCSS(shadow);
}
Expand All @@ -34,7 +36,11 @@ class CounterElement extends HTMLElement {
// Even if `ShadowRoot` is not a `HTMLElement`, we can cast it to `HTMLElement` to make TypeScript happy.
// It works anyway.
this.abortController = start(div, Counter, undefined, {
log: (l) => console.log(l),
log: {
error: (l) => console.error(l),
info: (l) => console.info(l),
debug: (l) => console.debug(l),
},
domains: {
// Create the Counter domain with the initial value and the external event target
counter: (et) =>
Expand All @@ -53,7 +59,9 @@ class CounterElement extends HTMLElement {
// This method is called when the `value` attribute changes
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
this.externalEventTarget.dispatchEvent(
new ExternalChangeValue({ newValue: Number(newValue) }),
new ExternalChangeValue({
newValue: Number(newValue),
}),
);
}

Expand Down
5 changes: 5 additions & 0 deletions examples/e-commerce/src/pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,10 @@ export async function Login(this: SeqflowFunctionContext) {
break;
}

this.app.log.info({
message: "User logged in",
data: { user },
});

this.app.router.navigate("/");
}
6 changes: 5 additions & 1 deletion examples/empty/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ import { Main } from "./Main";
import "./index.css";

start(document.getElementById("root"), Main, undefined, {
log: (l) => console.log(l),
log: {
error: (l) => console.error(l),
info: (l) => console.info(l),
debug: (l) => console.debug(l),
},
});
6 changes: 5 additions & 1 deletion examples/random-quote/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { Main } from "./Main";
import "./index.css";

start(document.getElementById("root"), Main, undefined, {
log: (l) => console.log(l),
log: {
error: (l) => console.error(l),
info: (l) => console.info(l),
debug: (l) => console.debug(l),
},
config: {
api: {
baseUrl: "https://api.quotable.io",
Expand Down
6 changes: 5 additions & 1 deletion examples/random-quote/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ afterAll(() => server.close());

test("should render the quote and refresh it", async () => {
start(document.body, Main, undefined, {
// log: (l) => console.log(l),
// log: {
// error: (l) => console.error(l),
// info: (l) => console.info(l),
// debug: (l) => console.debug(l),
// },
config: {
api: {
// Route to the mock server
Expand Down
90 changes: 59 additions & 31 deletions packages/seqflow-js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ export type DomainEvent<T> = DomainsPackage.DomainEvent<T>;
export const createDomainEventClass = DomainsPackage.createDomainEventClass;

export interface Log {
type: "info" | "debug" | "error";
message: string;
data?: unknown;
}
export type LogFunction = (log: Log) => void;
export interface LogFunction {
info: (l: Log) => void;
error: (l: Log) => void;
debug: (l: Log) => void;
}

export interface SeqflowAppContext {
log: LogFunction;
Expand Down Expand Up @@ -117,7 +120,9 @@ export interface SeqflowFunctionContext {
*/
createDOMFragment({
children,
}: { children?: ChildenType[] }): DocumentFragment;
}: {
children?: ChildenType[];
}): DocumentFragment;
}
export type SeqflowFunction<T> = (
this: SeqflowFunctionContext,
Expand Down Expand Up @@ -149,8 +154,7 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
componentOption: T | undefined,
) {
const componentName = component.name;
parentContext.app.log({
type: "debug",
parentContext.app.log.debug?.({
message: "startComponent",
data: { componentOption, componentName },
});
Expand Down Expand Up @@ -213,8 +217,7 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
) {
const oldChildIndex = componentChildren.findIndex((c) => c.key === key);
if (oldChildIndex < 0) {
this.app.log({
type: "error",
this.app.log.error?.({
message: "replaceChild: wrapper not found",
data: { key, newChild },
});
Expand Down Expand Up @@ -264,8 +267,7 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
return domainEvent(b.t, eventTarget);
},
navigationEvent(): EventAsyncGenerator<NavigationEvent> {
this.app.log({
type: "debug",
this.app.log.debug?.({
message: "navigationEvent",
data: {
componentName,
Expand All @@ -283,8 +285,7 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
this: SeqflowFunctionContext,
{ children }: { children?: ChildenType[] },
): DocumentFragment {
this.app.log({
type: "debug",
this.app.log.debug?.({
message: "createDOMFragment",
data: { children },
});
Expand Down Expand Up @@ -313,8 +314,7 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
continue;
}

this.app.log({
type: "error",
this.app.log.error?.({
message: "Unsupported child type. Implement me",
data: { child, children },
});
Expand All @@ -329,8 +329,7 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
options?: { [key: string]: string },
...children: ChildenType[]
): Node {
this.app.log({
type: "debug",
this.app.log.debug?.({
message: "createDOMElement",
data: { tagName, options, children },
});
Expand Down Expand Up @@ -381,10 +380,15 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
continue;
}

this.app.log({
type: "error",
this.app.log.error?.({
message: "Unsupported child type. Implement me",
data: { child, children, tagName, options, el },
data: {
child,
children,
tagName,
options,
el,
},
});
throw new Error("Unsupported child type");
}
Expand All @@ -406,7 +410,10 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
wrapper.classList.add(options.wrapperClass);
}

startComponent(childContext, wrapper, tagName, { ...options, children });
startComponent(childContext, wrapper, tagName, {
...options,
children,
});
return wrapper;
},
renderSync(this: SeqflowFunctionContext, html: string | JSX.Element) {
Expand All @@ -423,15 +430,16 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
if (v.then !== undefined) {
v.then(
() => {
parentContext.app.log({
type: "debug",
parentContext.app.log.debug?.({
message: "Component rendering ended",
data: { componentOption, componentName },
data: {
componentOption,
componentName,
},
});
},
(e) => {
parentContext.app.log({
type: "error",
parentContext.app.log.error?.({
message: "Component throws an error",
data: {
componentOption,
Expand All @@ -449,12 +457,16 @@ function startComponent<T extends { children?: ChildenType[]; key?: string }>(
export function start<
Component extends SeqflowFunction<FirstComponentData>,
// biome-ignore lint/suspicious/noExplicitAny: We don't care about the component properties
FirstComponentData extends Record<string, any> & { children?: ChildenType[] },
FirstComponentData extends Record<string, any> & {
children?: ChildenType[];
},
>(
root: HTMLElement,
firstComponent: Component,
componentOption: FirstComponentData | undefined,
seqflowConfiguration: Partial<SeqflowConfiguration>,
seqflowConfiguration: Partial<
Omit<SeqflowConfiguration, "log"> & { log?: Partial<LogFunction> }
>,
): AbortController {
const seqflowConfig = applyDefaults(seqflowConfiguration);

Expand All @@ -472,10 +484,12 @@ export function start<
router: seqflowConfig.router,
};
seqflowConfig.router.getEventTarget().addEventListener("navigation", (ev) => {
appContext.log({
type: "info",
appContext.log.info?.({
message: "navigate",
data: { path: (ev as NavigationEvent).path, event: ev },
data: {
path: (ev as NavigationEvent).path,
event: ev,
},
});
});

Expand Down Expand Up @@ -541,18 +555,32 @@ export function start<
}

function applyDefaults(
seqflowConfiguration: Partial<SeqflowConfiguration>,
seqflowConfiguration: Partial<
Omit<SeqflowConfiguration, "log"> & { log?: Partial<LogFunction> }
>,
): SeqflowConfiguration {
function noop() {}

if (seqflowConfiguration.router === undefined) {
seqflowConfiguration.router = new BrowserRouter(new EventTarget());
}

if (seqflowConfiguration.log === undefined) {
seqflowConfiguration.log = {};
}
if (seqflowConfiguration.log.info === undefined) {
seqflowConfiguration.log.info = noop;
}
if (seqflowConfiguration.log.error === undefined) {
seqflowConfiguration.log.error = noop;
}
if (seqflowConfiguration.log.debug === undefined) {
seqflowConfiguration.log.debug = noop;
}

return Object.assign(
{},
{
log: noop,
config: {},
domains: {},
},
Expand Down
58 changes: 41 additions & 17 deletions packages/seqflow-js/tests/appConfig.test.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,81 @@
import { screen, waitFor } from "@testing-library/dom";
import { expect, test } from "vitest";
import { beforeAll, beforeEach, expect, test, vi } from "vitest";

import { type Log, type SeqflowFunctionContext, start } from "../src/index";

const consoleInfoSpy = vi.spyOn(console, "info");

beforeEach(() => {
vi.clearAllMocks();
});

test("app log", async () => {
async function App(this: SeqflowFunctionContext) {
this.app.log({ type: "info", message: "render" });
this.app.log.info({ message: "render" });
this.renderSync("");
}

const logs: Log[] = [];
start(
document.body,
App,
{},
{
log: (l: Log) => {
logs.push(l);
log: {
error: (l: Log) => console.error(l),
info: (l: Log) => console.info(l),
debug: (l: Log) => console.debug(l),
},
},
);
expect(document.body.innerHTML).toBe("");
expect(consoleInfoSpy).toHaveBeenCalledOnce();
expect(consoleInfoSpy).toHaveBeenCalledWith({ message: "render" });
});

const appLog = logs.find((l) => l.message === "render" && l.type === "info");
expect(appLog).toBeDefined();
test("app log - partial", async () => {
async function App(this: SeqflowFunctionContext) {
this.app.log.error({ message: "render" });
this.app.log.info({ message: "render" });
this.app.log.debug({ message: "render" });
this.renderSync("1");
}

start(
document.body,
App,
{},
{
log: {},
},
);
expect(document.body.innerHTML).toBe("1");
});

test("app config", async () => {
async function App(this: SeqflowFunctionContext) {
this.app.log({ type: "info", message: this.app.config.api });
this.app.log.info({ message: this.app.config.api });
this.renderSync("");
}

const logs: Log[] = [];
start(
document.body,
App,
{},
{
log: (l: Log) => {
logs.push(l);
log: {
error: (l: Log) => console.error(l),
info: (l: Log) => console.info(l),
debug: (l: Log) => console.debug(l),
},
config: {
api: "https://api.example.com",
},
},
);
expect(document.body.innerHTML).toBe("");

const appLog = logs.find(
(l) => l.message === "https://api.example.com" && l.type === "info",
);
expect(appLog).toBeDefined();
expect(consoleInfoSpy).toHaveBeenCalledOnce();
expect(consoleInfoSpy).toHaveBeenCalledWith({
message: "https://api.example.com",
});
});

declare module "../src/index" {
Expand Down
Loading

0 comments on commit f551486

Please sign in to comment.