Support for putting ctx within jdd context

Reviewers: #reviewers

Subscribers: jenkins-user

Differential Revision: https://hub.sealcode.org/D1452
This commit is contained in:
Kuba Orlik 2024-04-30 18:36:16 +02:00
parent 5a704c3d48
commit 0b3129dcc0
12 changed files with 105 additions and 40 deletions

8
package-lock.json generated
View File

@ -16,7 +16,7 @@
"@playwright/test": "^1.36.1",
"@sealcode/file-manager": "^1.0.2",
"@sealcode/jdd": "^0.4.5",
"@sealcode/sealgen": "^0.15.6",
"@sealcode/sealgen": "^0.15.8",
"@sealcode/ts-predicates": "^0.6.2",
"@types/kill-port": "^2.0.0",
"@types/leaflet": "^1.9.8",
@ -844,9 +844,9 @@
"integrity": "sha512-EZI7e8EY8gI1pw2bKdevjl+fBJbcSlpNkCZ8XoEOV3cHakPujiT6M4l775RDkfxJSbLX7jhOBkhgPNDfmCpZbg=="
},
"node_modules/@sealcode/sealgen": {
"version": "0.15.6",
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.15.6.tgz",
"integrity": "sha512-XVmgGwSjzxQXEscvvHiAT6CTcuIV9c4Klb0pDYqNRtXlvZsoJHbhTsp+1Uua9vQh0NRe6Y+V2airw8eLr+ZZpg==",
"version": "0.15.8",
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.15.8.tgz",
"integrity": "sha512-+1V6xqDuvXs1aD2HuUjKj6UAnZ/HOajzENFv+y71nugs9F64/wT25kJxPeXf35sUIHLT9qU5af8q0XhiaIa4Ug==",
"dependencies": {
"@koa/router": "^12.0.1",
"@sealcode/file-manager": "^1.0.2",

View File

@ -30,7 +30,7 @@
"tmux-scripts": {
"watch": [
"npm run typecheck:back -- --watch",
"npm run build -- --watch",
"npx sealgen build --watch",
"npm run typecheck:front -- --watch",
"npm run start-watch"
]
@ -44,7 +44,7 @@
"@playwright/test": "^1.36.1",
"@sealcode/file-manager": "^1.0.2",
"@sealcode/jdd": "^0.4.5",
"@sealcode/sealgen": "^0.15.6",
"@sealcode/sealgen": "^0.15.8",
"@sealcode/ts-predicates": "^0.6.2",
"@types/kill-port": "^2.0.0",
"@types/leaflet": "^1.9.8",

7
src/back/declarations.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
import type { BaseContext } from "koa";
declare module "@sealcode/jdd" {
export interface JDDContext {
ctx: BaseContext;
}
}

View File

@ -1,16 +1,20 @@
import type { BaseContext } from "koa";
import type { JDDContext } from "@sealcode/jdd";
import type { FilePointer } from "@sealcode/file-manager";
import { makeSimpleJDDContext } from "@sealcode/jdd";
import { TheFileManager } from "./file-manager.js";
import { imageRouter } from "./image-router.js";
export const jdd_context: JDDContext = {
...makeSimpleJDDContext(TheFileManager),
render_image: async (image: string | FilePointer | null, args) => {
if (!image) {
return "";
}
const file = await TheFileManager.toPointer(image);
return imageRouter.image(await file.getPath(), args);
},
};
export function makeJDDContext(ctx: BaseContext): JDDContext {
return {
...makeSimpleJDDContext(TheFileManager),
render_image: async (image: string | FilePointer | null, args) => {
if (!image) {
return "";
}
const file = await TheFileManager.toPointer(image);
return imageRouter.image(await file.getPath(), args);
},
ctx,
};
}

View File

@ -3,9 +3,9 @@ import { TempstreamJSX } from "tempstream";
import { Page } from "@sealcode/sealgen";
import html from "../html.js";
import { registry } from "../jdd-components/components.js";
import { jdd_context } from "../jdd-context.js";
import { render } from "@sealcode/jdd";
import { shuffle } from "../util.js";
import { makeJDDContext } from "../jdd-context.js";
export const actionName = "AllComponents";
@ -17,6 +17,7 @@ export default new (class AllComponentsPage extends Page {
async render(ctx: Context) {
const components = registry.getAll();
const jdd_context = makeJDDContext(ctx);
const document = await Promise.all(
shuffle(Object.entries(components)).map(
async ([component_name, component]) => {

View File

@ -1,24 +1,28 @@
import type { Image } from "@sealcode/jdd";
import type { BaseContext } from "koa";
import type { StatefulPage } from "@sealcode/sealgen";
import { TempstreamJSX } from "tempstream";
import { jdd_context } from "../../jdd-context.js";
import type { ComponentPreviewState } from "../components.sreact.js";
import type { ComponentPreviewActions } from "./component-preview-actions.js";
import { printArgPath } from "./print-arg-path.js";
import { htmlEscape } from "escape-goat";
import type { FilePointer } from "@sealcode/file-manager";
import { makeJDDContext } from "../../jdd-context.js";
export function ComponentInputImage<State extends ComponentPreviewState>({
arg_path,
arg,
value,
ctx,
}: {
state: State;
arg_path: string[];
arg: Image;
value: FilePointer | null;
page: StatefulPage<ComponentPreviewState, typeof ComponentPreviewActions>;
ctx: BaseContext;
}) {
const jdd_context = makeJDDContext(ctx);
return (
<div style="margin-bottom: 10px">
<label

View File

@ -1,5 +1,6 @@
import type { ComponentArgument, List } from "@sealcode/jdd";
import type { StatefulPage } from "@sealcode/sealgen";
import type { BaseContext } from "koa";
import { TempstreamJSX } from "tempstream";
import type { ComponentPreviewState } from "../components.sreact.js";
import { ComponentInput } from "./component-input.js";
@ -10,12 +11,14 @@ export async function ComponentInputList<
T extends ComponentArgument<unknown>
>({
state,
ctx,
arg_path,
arg,
value,
page,
}: {
state: State;
ctx: BaseContext;
arg_path: string[];
arg: List<T>;
value: T[];
@ -31,6 +34,7 @@ export async function ComponentInputList<
<div style="display: flex">
<ComponentInput
{...{
ctx,
state,
arg_path: [...arg_path, i.toString()],
arg: arg.item_type,

View File

@ -1,3 +1,4 @@
import type { BaseContext } from "koa";
import type { ComponentArgument, Structured } from "@sealcode/jdd";
import type { StatefulPage } from "@sealcode/sealgen";
import { TempstreamJSX } from "tempstream";
@ -9,6 +10,7 @@ export function ComponentInputStructured<
T extends Structured<Record<string, ComponentArgument<unknown>>>
>({
state,
ctx,
arg_path,
arg,
value,
@ -16,6 +18,7 @@ export function ComponentInputStructured<
page,
}: {
state: ComponentPreviewState;
ctx: BaseContext;
arg_path: string[];
arg: T;
value: Record<string, unknown>;
@ -29,6 +32,7 @@ export function ComponentInputStructured<
<div>
<ComponentInput
{...{
ctx,
state,
arg_path: [...arg_path, arg_name],
arg,

View File

@ -1,16 +1,17 @@
import type { Table, TableData } from "@sealcode/jdd";
import { isTableHeader } from "@sealcode/jdd";
import type { StatefulPage } from "@sealcode/sealgen";
import type { BaseContext } from "koa";
import { TempstreamJSX } from "tempstream";
import type { ComponentPreviewState } from "../components.sreact.js";
import { ComponentInput } from "./component-input.js";
import type { ComponentPreviewActions } from "./component-preview-actions.js";
import delete_column_icon from "./table-delete-column.svg";
import add_row_below_icon from "./table-add-row-below.svg";
import add_column_right_icon from "./table-add-column-right.svg";
import delete_row_icon from "./table-delete-row.svg";
import add_row_below_icon from "./table-add-row-below.svg";
import add_column_header_icon from "./table-add-row-header-below.svg";
import delete_column_icon from "./table-delete-column.svg";
import delete_row_icon from "./table-delete-row.svg";
import move_column_right_icon from "./table-move-column-right.svg";
import move_row_down_icon from "./table-move-row-down.svg";
@ -21,11 +22,13 @@ export async function ComponentInputTable<
>({
state,
arg_path,
ctx,
arg,
value,
page,
}: {
state: State;
ctx: BaseContext;
arg_path: string[];
arg: Table<CellType, HeaderType>;
value: TableData<CellType, HeaderType>;
@ -119,6 +122,7 @@ export async function ComponentInputTable<
<ComponentInput
{...{
state,
ctx,
arg_path: [
...arg_path,
"rows",
@ -136,6 +140,7 @@ export async function ComponentInputTable<
<td>
<ComponentInput
{...{
ctx,
state,
arg_path: [
...arg_path,

View File

@ -1,7 +1,9 @@
import type { TableData } from "@sealcode/jdd";
import type { FilePointer } from "@sealcode/file-manager";
import type { ComponentArgument, TableData } from "@sealcode/jdd";
import { ComponentArguments, Enum, Image, List, Structured, Table } from "@sealcode/jdd";
import type { ComponentArgument } from "@sealcode/jdd";
import type { StatefulPage } from "@sealcode/sealgen";
import { is, predicates } from "@sealcode/ts-predicates";
import type { BaseContext } from "koa";
import { TempstreamJSX } from "tempstream";
import type { ComponentPreviewState } from "../components.sreact.js";
import { ComponentInputEnum } from "./component-input-enum.js";
@ -9,15 +11,14 @@ import { ComponentInputImage } from "./component-input-image.js";
import { ComponentInputList } from "./component-input-list.js";
import { ComponentInputStructured } from "./component-input-structured.js";
import { ComponentInputTable } from "./component-input-table.js";
import { printArgPath } from "./print-arg-path.js";
import type { ComponentPreviewActions } from "./component-preview-actions.js";
import { is, predicates } from "@sealcode/ts-predicates";
import type { FilePointer } from "@sealcode/file-manager";
import { printArgPath } from "./print-arg-path.js";
export const actionName = "Components";
const absoluteUrlPattern = "http(s?)(://)((www.)?)(([^.]+).)?([a-zA-z0-9-_]+)";
export function ComponentInput<State extends ComponentPreviewState, T>({
ctx,
state,
arg_path,
arg,
@ -25,6 +26,7 @@ export function ComponentInput<State extends ComponentPreviewState, T>({
page,
}: {
state: State;
ctx: BaseContext;
arg_path: string[];
arg: ComponentArgument<T>;
value: T;
@ -35,7 +37,15 @@ export function ComponentInput<State extends ComponentPreviewState, T>({
}
if (arg instanceof List) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return ComponentInputList({ state, arg_path, arg, value: value as T[], page });
return ComponentInputList({
ctx,
state,
arg_path,
arg,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
value: value as T[],
page,
});
}
const argType = arg.getTypeName();
@ -45,6 +55,7 @@ export function ComponentInput<State extends ComponentPreviewState, T>({
if (arg instanceof Structured) {
return ComponentInputStructured({
ctx,
state,
arg_path,
arg,
@ -67,6 +78,7 @@ export function ComponentInput<State extends ComponentPreviewState, T>({
if (arg instanceof Image) {
return ComponentInputImage({
ctx,
state,
arg_path,
arg,
@ -78,6 +90,7 @@ export function ComponentInput<State extends ComponentPreviewState, T>({
if (arg instanceof Table) {
return ComponentInputTable({
ctx,
state,
arg_path,
arg,

View File

@ -1,9 +1,10 @@
import type { ComponentArgument, List, Table, TableData } from "@sealcode/jdd";
import type { BaseContext } from "koa";
import { isTableData, isTableRegularRow } from "@sealcode/jdd";
import objectPath from "object-path";
import { registry } from "../../jdd-components/components.js";
import { jdd_context } from "../../jdd-context.js";
import type { ComponentPreviewState } from "../components.sreact.js";
import { makeJDDContext } from "../../jdd-context.js";
function moveElement<T>(array: Array<T>, fromIndex: number, toIndex: number): Array<T> {
const element = array.splice(fromIndex, 1)[0];
@ -13,6 +14,7 @@ function moveElement<T>(array: Array<T>, fromIndex: number, toIndex: number): Ar
export const ComponentPreviewActions = <const>{
add_array_item: async (
ctx: BaseContext,
state: ComponentPreviewState,
_: Record<string, string>,
arg_path: string[]
@ -35,7 +37,7 @@ export const ComponentPreviewActions = <const>{
objectPath.insert(
component_args,
arg_path,
await argument.item_type.getExampleValue(jdd_context),
await argument.item_type.getExampleValue(makeJDDContext(ctx)),
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
((objectPath.get(component_args, arg_path) as unknown[]) || []).length
);
@ -45,6 +47,7 @@ export const ComponentPreviewActions = <const>{
};
},
remove_array_item: (
_ctx: BaseContext,
state: ComponentPreviewState,
_: Record<string, string>,
arg_path: string[],
@ -58,6 +61,7 @@ export const ComponentPreviewActions = <const>{
};
},
change_component: async (
ctx: BaseContext,
state: ComponentPreviewState,
inputs: Record<string, string>
) => {
@ -66,10 +70,12 @@ export const ComponentPreviewActions = <const>{
return {
...state,
component: component_name,
component_args: (await component?.getExampleValues(jdd_context)) || {},
component_args:
(await component?.getExampleValues(makeJDDContext(ctx))) || {},
};
},
randomize_args: async (
ctx: BaseContext,
state: ComponentPreviewState,
inputs: Record<string, string>
) => {
@ -77,16 +83,19 @@ export const ComponentPreviewActions = <const>{
const component = registry.get(component_name);
return {
...state,
component_args: (await component?.getExampleValues(jdd_context)) || {},
component_args:
(await component?.getExampleValues(makeJDDContext(ctx))) || {},
};
},
add_table_row: async (
ctx: BaseContext,
state: ComponentPreviewState,
_: Record<string, string>,
arg_path: string[],
columns: number,
type: "header" | "row" = "row"
) => {
const jdd_context = makeJDDContext(ctx);
const component_args = state.component_args;
let row;
const component = registry.get(state.component);
@ -131,10 +140,12 @@ export const ComponentPreviewActions = <const>{
return result;
},
add_table_column: async (
ctx: BaseContext,
state: ComponentPreviewState,
_: Record<string, string>,
arg_path: string[]
) => {
const jdd_context = makeJDDContext(ctx);
const component_args = state.component_args;
const component = registry.get(state.component);
if (!component) {
@ -170,6 +181,7 @@ export const ComponentPreviewActions = <const>{
},
remove_table_column: (
_ctx: BaseContext,
state: ComponentPreviewState,
_: Record<string, string>,
arg_path: string[],
@ -198,6 +210,7 @@ export const ComponentPreviewActions = <const>{
};
},
remove_table_row: (
_ctx: BaseContext,
state: ComponentPreviewState,
_: Record<string, string>,
arg_path: string[],
@ -212,6 +225,7 @@ export const ComponentPreviewActions = <const>{
return result;
},
move_table_column_right: (
_ctx: BaseContext,
state: ComponentPreviewState,
_: Record<string, string>,
arg_path: string[],
@ -237,6 +251,7 @@ export const ComponentPreviewActions = <const>{
},
move_table_row_down: (
_ctx: BaseContext,
state: ComponentPreviewState,
_: Record<string, string>,
arg_path: string[],
@ -257,7 +272,11 @@ export const ComponentPreviewActions = <const>{
return result;
},
change_size: (state: ComponentPreviewState, inputs: Record<string, string>) => {
change_size: (
_ctx: BaseContext,
state: ComponentPreviewState,
inputs: Record<string, string>
) => {
return {
...state,
...inputs,

View File

@ -6,7 +6,7 @@ import type { Templatable } from "tempstream";
import { tempstream, TempstreamJSX } from "tempstream";
import html, { defaultHead } from "../html.js";
import { registry } from "../jdd-components/components.js";
import { jdd_context } from "../jdd-context.js";
import { makeJDDContext } from "../jdd-context.js";
import { ComponentInput } from "./component-preview/component-input.js";
import { ComponentPreviewActions } from "./component-preview/component-preview-actions.js";
@ -24,23 +24,23 @@ export default new (class ComponentsPage extends StatefulPage<
> {
actions = ComponentPreviewActions;
async getInitialState() {
async getInitialState(ctx: BaseContext) {
const [component_name, component] = Object.entries(registry.getAll())[0];
const initial_state = {
component: component_name,
component_args: await component.getExampleValues(jdd_context),
component_args: await component.getExampleValues(makeJDDContext(ctx)),
};
return initial_state;
}
async serializeState(_context: BaseContext, state: ComponentPreviewState) {
async serializeState(ctx: BaseContext, state: ComponentPreviewState) {
const component = registry.get(state.component);
const result = JSON.stringify({
...state,
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
component_args: component
? await component.convertParsedToStorage(
jdd_context,
makeJDDContext(ctx),
state.component_args
)
: {},
@ -48,7 +48,7 @@ export default new (class ComponentsPage extends StatefulPage<
return result;
}
async deserializeState(_context: BaseContext, state_string: string) {
async deserializeState(ctx: BaseContext, state_string: string) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const raw = JSON.parse(state_string);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-member-access
@ -60,7 +60,7 @@ export default new (class ComponentsPage extends StatefulPage<
component_args: component
? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
await component.convertStorageToParsed(
jdd_context,
makeJDDContext(ctx),
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
raw.component_args || {}
)
@ -94,15 +94,17 @@ export default new (class ComponentsPage extends StatefulPage<
args: state.component_args,
},
],
jdd_context
makeJDDContext(ctx)
)}`
);
}
async preprocessOverrides(
ctx: BaseContext,
state: ComponentPreviewState,
overrides: Record<string, unknown>
) {
const jdd_context = makeJDDContext(ctx);
const component_name = state.component;
if (!component_name) {
throw new Error("Unspecified component name");
@ -131,6 +133,7 @@ export default new (class ComponentsPage extends StatefulPage<
containerSizes = ["320", "600", "800", "1024", "1300", "1920"];
render(ctx: BaseContext, state: ComponentPreviewState) {
const jdd_context = makeJDDContext(ctx);
const all_components = registry.getAll();
const component =
registry.get(state.component) || Object.values(all_components)[0];
@ -163,6 +166,7 @@ export default new (class ComponentsPage extends StatefulPage<
<ComponentInput
{...{
state,
ctx,
arg_path: [arg_name],
arg,
value: