Skip to content

Commit

Permalink
feat: function v2
Browse files Browse the repository at this point in the history
  • Loading branch information
stefan-gorules committed Jul 10, 2024
1 parent 6de8918 commit f12acea
Show file tree
Hide file tree
Showing 12 changed files with 3,100 additions and 41 deletions.
2 changes: 1 addition & 1 deletion packages/jdm-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"fast-deep-equal": "^3.1.3",
"immer": "10.1.1",
"json5": "^2.2.3",
"monaco-editor": "^0.48.0",
"monaco-editor": "^0.50.0",
"react-ace": "^11.0.1",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "16.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Monaco } from '@monaco-editor/react';
import equal from 'fast-deep-equal/es6/react';
import { produce } from 'immer';
import type { WritableDraft } from 'immer/src/types/types-external';
Expand Down Expand Up @@ -109,6 +110,7 @@ export type DecisionGraphStoreType = {
onPanelsChange?: (val?: string) => void;
onReactFlowInit?: (instance: ReactFlowInstance) => void;
onCodeExtension?: CodeEditorProps['extension'];
onFunctionReady?: (monaco: Monaco) => void;
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type DecisionGraphEmptyType = {
onReactFlowInit?: DecisionGraphStoreType['listeners']['onReactFlowInit'];

onCodeExtension?: DecisionGraphStoreType['listeners']['onCodeExtension'];
onFunctionReady?: DecisionGraphStoreType['listeners']['onFunctionReady'];
};

export const DecisionGraphEmpty: React.FC<DecisionGraphEmptyType> = ({
Expand All @@ -49,6 +50,7 @@ export const DecisionGraphEmpty: React.FC<DecisionGraphEmptyType> = ({
onPanelsChange,
onReactFlowInit,
onCodeExtension,
onFunctionReady,
}) => {
const mountedRef = useRef(false);
const graphActions = useDecisionGraphActions();
Expand Down Expand Up @@ -83,8 +85,9 @@ export const DecisionGraphEmpty: React.FC<DecisionGraphEmptyType> = ({
onReactFlowInit,
onPanelsChange,
onCodeExtension,
onFunctionReady,
});
}, [onReactFlowInit, onPanelsChange, onCodeExtension]);
}, [onReactFlowInit, onPanelsChange, onCodeExtension, onFunctionReady]);

useEffect(() => {
listenerStore.setState({ onChange: innerChange });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Monaco } from '@monaco-editor/react';
import { Spin } from 'antd';
import React, { Suspense } from 'react';
import React, { Suspense, useEffect, useState } from 'react';
import { P, match } from 'ts-pattern';

import { useDecisionGraphActions, useDecisionGraphState } from '../context/dg-store.context';
import { useDecisionGraphActions, useDecisionGraphListeners, useDecisionGraphState } from '../context/dg-store.context';
import type { SimulationTrace, SimulationTraceDataFunction } from '../types/simulation.types';

const Function = React.lazy(async () => {
Expand All @@ -16,20 +17,54 @@ export type TabFunctionProps = {

export const TabFunction: React.FC<TabFunctionProps> = ({ id }) => {
const graphActions = useDecisionGraphActions();
const { nodeTrace, disabled, content } = useDecisionGraphState(
const onFunctionReady = useDecisionGraphListeners((s) => s.onFunctionReady);
const [monaco, setMonaco] = useState<Monaco>();
const { nodeTrace, disabled, content, additionalModules } = useDecisionGraphState(
({ simulate, disabled, configurable, decisionGraph }) => ({
nodeTrace: match(simulate)
.with({ result: P._ }, ({ result }) => result?.trace?.[id])
.otherwise(() => null),
disabled,
configurable,
content: (decisionGraph?.nodes ?? []).find((node) => node.id === id)?.content,
additionalModules: (decisionGraph?.nodes ?? [])
.filter((node) => node.type === 'functionNode')
.map((node) => ({
id: node.id,
name: node.name,
content: node.content,
})),
}),
);

useEffect(() => {
if (!monaco) {
return;
}

const extraLibs = monaco.languages.typescript.javascriptDefaults.getExtraLibs();
const newExtraLibs = Object.entries(extraLibs)
.map(([key, value]) => ({
filePath: key,
content: value.content,
}))
.filter((i) => !i.filePath.startsWith('node:'));

additionalModules.forEach((module) => {
newExtraLibs.push({
filePath: `node:${module.name}`,
content: `declare module 'node:${module.name}' { ${module.content} }`,
});
});

monaco.languages.typescript.javascriptDefaults.setExtraLibs(newExtraLibs);
onFunctionReady?.(monaco);
}, [JSON.stringify(additionalModules), monaco, onFunctionReady]);

return (
<Suspense fallback={<Spin />}>
<Function
onMonacoReady={(monaco) => setMonaco(monaco)}
value={typeof content === 'string' ? content : ''}
onChange={(val) => {
graphActions.updateNode(id, (draft) => {
Expand Down
36 changes: 28 additions & 8 deletions packages/jdm-editor/src/components/function/function.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FormatPainterOutlined } from '@ant-design/icons';
import { Editor, useMonaco } from '@monaco-editor/react';
import { Editor, type Monaco, useMonaco } from '@monaco-editor/react';
import { Button, Spin, theme } from 'antd';
import type { editor } from 'monaco-editor';
import React, { useEffect, useRef, useState } from 'react';
Expand All @@ -20,6 +20,7 @@ export type FunctionProps = {
value?: string;
onChange?: (value: string) => void;
trace?: SimulationTrace<SimulationTraceDataFunction>;
onMonacoReady?: (monaco: Monaco) => void;
};

export const Function: React.FC<FunctionProps> = ({
Expand All @@ -30,6 +31,7 @@ export const Function: React.FC<FunctionProps> = ({
value,
onChange,
trace,
onMonacoReady,
}) => {
const monaco = useMonaco();
const mountedRef = useRef(false);
Expand All @@ -51,14 +53,32 @@ export const Function: React.FC<FunctionProps> = ({
useEffect(() => {
if (!monaco) return;

const extraLibs = Object.keys(functionDefinitions).map(
(pkg: keyof typeof functionDefinitions) => ({
content: `declare module '${pkg}' { ${functionDefinitions[pkg]} }`,
}),
{},
);
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
module: monaco.languages.typescript.ModuleKind.ESNext,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
allowSyntheticDefaultImports: true,
allowNonTsExtensions: true,
allowJs: true,
checkJs: true,
});

monaco.languages.typescript.javascriptDefaults.setExtraLibs(extraLibs);
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSyntaxValidation: false,
noSemanticValidation: false,
noSuggestionDiagnostics: false,
onlyVisible: false,
});

Object.entries(functionDefinitions.libs).forEach(([pkg, types]) => {
monaco.languages.typescript.javascriptDefaults.addExtraLib(`declare module '${pkg}' { ${types} }`, pkg);
});

Object.entries(functionDefinitions.globals).forEach(([pkg, types]) => {
monaco.languages.typescript.javascriptDefaults.addExtraLib(types, `ts:${pkg}`);
});

onMonacoReady?.(monaco);
}, [monaco]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
/**
* @param input
* @param {{
* dayjs: import('dayjs')
* Big: import('big.js').BigConstructor
* }} helpers
*/
const handler = (input, { dayjs, Big }) => {
import http from 'http';
import zen from 'zen';

export const handler = async (input) => {
const a = zen.evaluate();
return input;
};
11 changes: 11 additions & 0 deletions packages/jdm-editor/src/components/function/helpers/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
declare namespace console {
function log(...args: any[]): void;
}

interface Config {
readonly maxDepth: number;
readonly iteration: number;
readonly trace: boolean;
}

declare const config: Config;
28 changes: 28 additions & 0 deletions packages/jdm-editor/src/components/function/helpers/http.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
declare class HttpResponse {
readonly data: any;
readonly headers: Record<string, string>;
readonly status: number;
}

interface HttpConfig {
headers: Record<string, string>;
params: Record<string, string>;
data: any;
}

export class Http {
head(url: string, config?: HttpConfig): Promise<HttpResponse>;

get(url: string, config?: HttpConfig): Promise<HttpResponse>;

delete(url: string, config?: HttpConfig): Promise<HttpResponse>;

post(url: string, data: any, config?: HttpConfig): Promise<HttpResponse>;

patch(url: string, data: any, config?: HttpConfig): Promise<HttpResponse>;

put(url: string, data: any, config?: HttpConfig): Promise<HttpResponse>;
}

declare const http: Http;
export default http;
20 changes: 18 additions & 2 deletions packages/jdm-editor/src/components/function/helpers/libs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,26 @@ import dayjs from 'dayjs/index.d.ts?raw';

// @ts-ignore
import defaultFn from './default-function.js?raw';
// @ts-ignore
import globalDts from './global.d.ts?raw';
// @ts-ignore
import http from './http.d.ts?raw';
// @ts-ignore
import zen from './zen.d.ts?raw';
// @ts-ignore
import zod from './zod.d.ts?raw';

export const functionDefinitions = {
dayjs,
'big.js': bigJs,
libs: {
dayjs,
'big.js': bigJs,
zod,
http,
zen,
},
globals: {
'global.d.ts': globalDts,
},
};

export const defaultFunctionValue = defaultFn;
48 changes: 48 additions & 0 deletions packages/jdm-editor/src/components/function/helpers/zen.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
interface EvaluateOptions {
trace?: boolean;
}

interface EvaluateResponse {
performance: string;
result: any;
trace?: any;
}

interface ZenModule {
/**
* Evaluates ZEN expression
* @param expression
* @param context Must contain '$' key
*/
evaluateExpression(expression: string, context: any): any;

/**
* Evaluates ZEN unary expression
* @param expression
* @param context Must contain '$' key
*/
evaluateUnaryExpression(expression: string, context: any): boolean;

/**
* Evaluates ZEN unary expression
* @param file File to be evaluated via DecisionLoader
* @param context
* @param opts
*/
evaluate(file: string, context: any, opts?: EvaluateOptions): Promise<EvaluateResponse>;

/**
* Get Content from the DecisionLoader
* @param file
*/
get(file: string): Promise<any>;
}

export const evaluateExpression: ZenModule['evaluateExpression'];
export const evaluateUnaryExpression: ZenModule['evaluateUnaryExpression'];
export const evaluate: ZenModule['evaluate'];
export const get: ZenModule['get'];

declare const zenModule: ZenModule;

export default zenModule;
Loading

0 comments on commit f12acea

Please sign in to comment.