fix monaco

This commit is contained in:
Rizky 2023-10-26 05:46:54 +07:00
parent bf19dbf50f
commit a1c5166748
31 changed files with 877 additions and 119 deletions

View File

@ -9,7 +9,8 @@ import { EdMain } from "./panel/main/main";
import { EdPane } from "./panel/main/pane-resize"; import { EdPane } from "./panel/main/pane-resize";
import { EdPopCompGroup } from "./panel/popup/comp-group"; import { EdPopCompGroup } from "./panel/popup/comp-group";
import { EdPopSite } from "./panel/popup/site"; import { EdPopSite } from "./panel/popup/site";
import { jscript } from "../editor/panel/script/script-element"; import { EdScriptInit } from "./panel/script/monaco/init";
import { EdScriptSite } from "./panel/script/site";
export const EdBase = () => { export const EdBase = () => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");
@ -33,7 +34,6 @@ export const EdBase = () => {
); );
} }
const Editor = jscript.editor;
return ( return (
<div className="flex flex-col flex-1"> <div className="flex flex-col flex-1">
<div className="flex justify-between"></div> <div className="flex justify-between"></div>
@ -42,20 +42,11 @@ export const EdBase = () => {
<EdPane type="left" /> <EdPane type="left" />
<EdMain /> <EdMain />
</div> </div>
<> <>
<EdPopSite /> <EdPopSite />
<EdPopCompGroup /> <EdPopCompGroup />
{Editor && !jscript.ready && ( <EdScriptInit />
<div className="hidden"> <EdScriptSite />
<Editor
onMount={() => {
jscript.ready = true;
p.render();
}}
/>
</div>
)}
</> </>
</div> </div>
); );

View File

@ -69,6 +69,7 @@ export const EDGlobal = {
| "ready", | "ready",
sync: null as unknown as Awaited<ReturnType<typeof clientStartSync>>, sync: null as unknown as Awaited<ReturnType<typeof clientStartSync>>,
site: EmptySite, site: EmptySite,
script: { siteTypes: {} as Record<string, string> },
page: { page: {
cur: EmptyPage, cur: EmptyPage,
doc: null as null | DPage, doc: null as null | DPage,
@ -86,6 +87,9 @@ export const EDGlobal = {
group: {} as Record<string, Awaited<ReturnType<SAction["comp"]["group"]>>>, group: {} as Record<string, Awaited<ReturnType<SAction["comp"]["group"]>>>,
}, },
ui: { ui: {
script: {
site: false,
},
layout: { layout: {
left: parseInt(localStorage.getItem("prasi-layout-left") || "250"), left: parseInt(localStorage.getItem("prasi-layout-left") || "250"),
right: parseInt(localStorage.getItem("prasi-layout-right") || "250"), right: parseInt(localStorage.getItem("prasi-layout-right") || "250"),

View File

@ -1,6 +1,6 @@
import init from "wasm-gzip"; import init from "wasm-gzip";
import { PG } from "./ed-global"; import { PG } from "./ed-global";
import { jscript } from "../../editor/panel/script/script-element"; import { jscript } from "../../../utils/script/jscript";
export const edInit = async (p: PG) => { export const edInit = async (p: PG) => {
await init(); await init();

View File

@ -0,0 +1,102 @@
import { ReactNode } from "react";
import { useLocal } from "web-utils";
import { jscript } from "../../../../../utils/script/jscript";
import { Loading } from "../../../../../utils/ui/loading";
export const EdScriptInit = () => {
const Editor = jscript.editor;
const local = useLocal({ editorLoaded: false }, () => {});
jscript.events.editorLoaded = () => {
local.editorLoaded = true;
local.render();
};
return (
<>
{Editor && local.editorLoaded && (
<div className="hidden">
<Editor
onMount={() => {
jscript.events.pendingDone();
}}
/>
</div>
)}
</>
);
};
export const EdMonacoWrap = ({
children,
}: {
children: (Editor: Exclude<typeof jscript.editor, null>) => ReactNode;
}) => {
const local = useLocal({});
if (jscript.pending && (!jscript.editor || !jscript.build)) {
jscript.pending.then(() => {
local.render();
});
}
return (
<div
className={cx(
"flex flex-1 absolute inset-[80px]",
css`
.monaco-editor {
.mtk9 {
color: #022f62;
}
.mtk1 {
color: #022f62;
}
.mtk22 {
color: #015cc5;
}
.mtk8 {
color: #015cc5;
}
.mtk5 {
color: #55bb8a;
}
.monaco-editor.showUnused .squiggly-inline-unnecessary {
opacity: 0.4;
}
.jsx-expression-braces {
color: #7c3813;
}
.jsx-tag-angle-bracket {
color: #619ac3;
}
.jsx-tag-name {
color: #619ac3;
}
.jsx-tag-order-1 {
color: #23863a;
}
.jsx-tag-order-2 {
color: #4e7ca1;
}
.jsx-tag-order-3 {
color: #020360;
}
.jsx-tag-attribute-key {
color: #6f42c1;
}
.jsx-text {
color: #000000;
}
}
`
)}
>
{!jscript.editor || !jscript.build ? (
<Loading note="script-cst" backdrop={false} />
) : (
children(jscript.editor)
)}
</div>
);
};

View File

@ -0,0 +1,98 @@
import { jsMount } from "../../../../../utils/script/mount";
import { monacoTypings } from "../../../../../utils/script/typings";
import { Modal } from "../../../../../utils/ui/modal";
import { EdMonacoWrap } from "./init";
import type { Editor } from "@monaco-editor/react";
const monacoState = {} as Record<string, any>;
export const EdMonaco = (arg: {
id?: string;
type: "js" | "html" | "css";
filename: string;
monaco: Parameters<typeof Editor>[0];
onClose: () => void;
prop?: {
val: Record<string, any>;
types: Record<string, string>;
};
}) => {
const filename = arg.filename;
const m = arg.monaco;
const prop = { ...arg.monaco };
prop.options = {
minimap: { enabled: false },
wordWrap: "wordWrapColumn",
autoClosingBrackets: "always",
tabSize: 2,
autoIndent: "full",
formatOnPaste: true,
formatOnType: true,
useTabStops: true,
};
if (arg.type === "html") {
prop.language = "html";
}
if (arg.type === "css") {
prop.language = "scss";
}
if (arg.type === "js") {
prop.language = "typescript";
prop.onMount = async (editor, monaco) => {
const value = editor.getValue();
monaco.editor.getModels().forEach((model) => {
if (model.uri.toString().startsWith("inmemory://model")) {
model.dispose();
}
});
let model = monaco.editor.createModel(
value,
"typescript",
monaco.Uri.parse(`ts:${filename}`)
);
editor.setModel(model);
if (arg.id) {
if (!monacoState[arg.id]) {
editor.trigger("fold", "editor.foldAllMarkerRegions", null);
} else {
editor.restoreViewState(monacoState[arg.id]);
}
}
await jsMount(editor, monaco);
if (arg.prop)
await monacoTypings(
{
script: {
siteTypes: {},
},
site: {
api_url: "",
},
site_dts: "",
},
monaco,
{
values: arg.prop.val,
types: arg.prop.types,
}
);
if (m.onMount) m.onMount(editor, monaco);
};
}
return (
<Modal
open
onOpenChange={() => {
arg.onClose();
}}
>
<EdMonacoWrap>{(Editor) => <Editor {...prop} />}</EdMonacoWrap>
</Modal>
);
};

View File

@ -0,0 +1,36 @@
import { useGlobal } from "web-utils";
import { EdMonaco } from "./monaco/monaco";
import { EDGlobal } from "../../logic/ed-global";
export const EdScriptSite = () => {
const p = useGlobal(EDGlobal, "EDITOR");
if (!p.ui.script.site) {
return null;
}
return (
<EdMonaco
id="script-site"
type="js"
filename="site.tsx"
monaco={{
value: p.site.js,
onChange: (v) => {
console.log(v);
},
}}
prop={{
val: {},
types: {
exports: "any",
types: "any",
render: "()=>void",
},
}}
onClose={() => {
p.ui.script.site = false;
p.render();
}}
/>
);
};

View File

@ -1,3 +0,0 @@
export const EdScriptSite = () => {
};

View File

@ -1,8 +1,18 @@
import { useGlobal } from "web-utils";
import { TopBtn } from "../top-btn"; import { TopBtn } from "../top-btn";
import { EDGlobal } from "../../../logic/ed-global";
export const EdSiteJS = () => { export const EdSiteJS = () => {
const p = useGlobal(EDGlobal, "EDITOR");
return ( return (
<TopBtn style="slim" className="font-bold font-mono"> <TopBtn
style="slim"
className="font-bold font-mono"
onClick={() => {
p.ui.script.site = true;
p.render();
}}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="16" width="16"

View File

@ -12,12 +12,12 @@ import {
mergeScopeUpwards, mergeScopeUpwards,
treeScopeEval, treeScopeEval,
} from "../logic/tree-scope"; } from "../logic/tree-scope";
import { jscript } from "../panel/script/script-element";
import { fillID } from "../tools/fill-id"; import { fillID } from "../tools/fill-id";
import { newMap } from "../tools/yjs-tools"; import { newMap } from "../tools/yjs-tools";
import { ComponentOver, ElProp, createElProp } from "./e-relprop"; import { ComponentOver, ElProp, createElProp } from "./e-relprop";
import { ETextInternal } from "./e-text"; import { ETextInternal } from "./e-text";
import { DefaultScript } from "../panel/script/monaco/monaco-el"; import { DefaultScript } from "../panel/script/monaco/monaco-el";
import { jscript } from "../../../utils/script/jscript";
export const ERender: FC<{ export const ERender: FC<{
id: string; id: string;
@ -185,9 +185,7 @@ export const ERender: FC<{
meta.mitem meta.mitem
) { ) {
if (!jscript.build) { if (!jscript.build) {
jscript.init().then(() => { jscript.init(p.render);
p.render();
});
return null; return null;
} }
jscript jscript

View File

@ -7,10 +7,10 @@ import {
} from "../../../utils/script/init-api"; } from "../../../utils/script/init-api";
import { LSite } from "../../live/logic/global"; import { LSite } from "../../live/logic/global";
import { validateLayout } from "../../live/logic/layout"; import { validateLayout } from "../../live/logic/layout";
import { jscript } from "../panel/script/script-element";
import importModule from "../tools/dynamic-import"; import importModule from "../tools/dynamic-import";
import { PG } from "./global"; import { PG } from "./global";
import { devLoader } from "../../live/dev-loader"; import { devLoader } from "../../live/dev-loader";
import { jscript } from "../../../utils/script/jscript";
export const w = window as unknown as { export const w = window as unknown as {
basepath: string; basepath: string;
@ -46,6 +46,10 @@ export const initEditor = async (p: PG, site_id: string) => {
return ""; return "";
}; };
if (!jscript.pending) {
jscript.init(p.render);
}
if (!p.item) return; if (!p.item) return;
p.item.active = localStorage.getItem("prasi-item-active-id") || ""; p.item.active = localStorage.getItem("prasi-item-active-id") || "";
@ -147,10 +151,6 @@ export const initEditor = async (p: PG, site_id: string) => {
p.status = "ready"; p.status = "ready";
p.render(); p.render();
if (!jscript.build) {
jscript.init();
}
}; };
export const execSiteJS = (p: PG) => { export const execSiteJS = (p: PG) => {

View File

@ -5,6 +5,7 @@ import { mobileCSS } from "../elements/e-page";
import { editorStyle } from "../elements/style"; import { editorStyle } from "../elements/style";
import { EditorGlobal } from "../logic/global"; import { EditorGlobal } from "../logic/global";
import { Toolbar } from "./toolbar/Toolbar"; import { Toolbar } from "./toolbar/Toolbar";
import { EdScriptInit } from "../../ed/panel/script/monaco/init";
const ETree = lazy(async () => ({ const ETree = lazy(async () => ({
default: (await import("./tree/tree")).ETree, default: (await import("./tree/tree")).ETree,
@ -61,6 +62,7 @@ export const EMainEditor = () => {
</div> </div>
{p.status === "ready" && ( {p.status === "ready" && (
<Suspense fallback={<Loading note={`toolbar`} />}> <Suspense fallback={<Loading note={`toolbar`} />}>
<EdScriptInit />
{p.manager.site && <SiteManager />} {p.manager.site && <SiteManager />}
{p.manager.page && <PageManager />} {p.manager.page && <PageManager />}
{p.manager.comp && <CompManager />} {p.manager.comp && <CompManager />}

View File

@ -5,17 +5,12 @@ import type {
} from "@monaco-editor/react"; } from "@monaco-editor/react";
import { FC } from "react"; import { FC } from "react";
import { useGlobal, useLocal } from "web-utils"; import { useGlobal, useLocal } from "web-utils";
import { FBuild } from "../../../../../utils/script/jscript";
import { EditorGlobal } from "../../../logic/global"; import { EditorGlobal } from "../../../logic/global";
import { jsMount } from "./mount"; import { jsMount } from "./mount";
import { monacoTypings } from "./typings"; import { monacoTypings } from "./typings";
export type MonacoEditor = Parameters<OnMount>[0]; export type MonacoEditor = Parameters<OnMount>[0];
export type FBuild = (
entryFileName: string,
src: string,
files?: Record<string, string>
) => Promise<string>;
export const customMonacoState: Record<string, any> = {}; export const customMonacoState: Record<string, any> = {};
export const ScriptMonacoCustom: FC<{ export const ScriptMonacoCustom: FC<{

View File

@ -7,18 +7,19 @@ import strDelta from "textdiff-create";
import { useGlobal, useLocal } from "web-utils"; import { useGlobal, useLocal } from "web-utils";
import * as Y from "yjs"; import * as Y from "yjs";
import { TypedMap } from "yjs-types"; import { TypedMap } from "yjs-types";
import { FBuild } from "../../../../../utils/script/jscript";
import { FMCompDef, FNAdv } from "../../../../../utils/types/meta-fn"; import { FMCompDef, FNAdv } from "../../../../../utils/types/meta-fn";
import { Button } from "../../../../../utils/ui/form/Button";
import { Loading } from "../../../../../utils/ui/loading"; import { Loading } from "../../../../../utils/ui/loading";
import { Popover } from "../../../../../utils/ui/popover";
import { EditorGlobal } from "../../../logic/global"; import { EditorGlobal } from "../../../logic/global";
import { mergeScopeUpwards } from "../../../logic/tree-scope"; import { mergeScopeUpwards } from "../../../logic/tree-scope";
import { newMap } from "../../../tools/yjs-tools"; import { newMap } from "../../../tools/yjs-tools";
import { MonacoElHistory } from "./monaco-el-history";
import { MonacoElSnippet } from "./monaco-el-snippet";
import { jsMount } from "./mount"; import { jsMount } from "./mount";
import { MonacoScopeBar } from "./scope-bar"; import { MonacoScopeBar } from "./scope-bar";
import { monacoTypings } from "./typings"; import { monacoTypings } from "./typings";
import { MonacoElSnippet } from "./monaco-el-snippet";
import { Button } from "../../../../../utils/ui/form/Button";
import { Popover } from "../../../../../utils/ui/popover";
import { MonacoElHistory } from "./monaco-el-history";
export type MonacoEditor = Parameters<OnMount>[0]; export type MonacoEditor = Parameters<OnMount>[0];
export const DefaultScript = { export const DefaultScript = {
@ -42,13 +43,6 @@ const w = window as unknown as {
}; };
}; };
export type FBuild = (
entryFileName: string,
src: string,
files?: Record<string, string>,
verbose?: boolean
) => Promise<string>;
const monacoViewState = {} as Record<string, any>; const monacoViewState = {} as Record<string, any>;
export const ScriptMonacoElement: FC<{ export const ScriptMonacoElement: FC<{

View File

@ -5,7 +5,7 @@ import { Loading } from "../../../../utils/ui/loading";
import { EditorGlobal } from "../../logic/global"; import { EditorGlobal } from "../../logic/global";
import { ScriptMonacoCustom } from "./monaco/monaco-custom"; import { ScriptMonacoCustom } from "./monaco/monaco-custom";
import { MonacoEditor } from "./monaco/typings"; import { MonacoEditor } from "./monaco/typings";
import { jscript } from "./script-element"; import { jscript } from "../../../../utils/script/jscript";
export const EScriptCustom: FC<{ export const EScriptCustom: FC<{
monaco_id: string; monaco_id: string;
@ -28,16 +28,8 @@ export const EScriptCustom: FC<{
}) => { }) => {
const p = useGlobal(EditorGlobal); const p = useGlobal(EditorGlobal);
if (!jscript.editor) { if (!jscript.editor && jscript.pending) {
Promise.all([ jscript.pending.then(() => p.render());
import("@monaco-editor/react").then((e) => {
jscript.editor = e.Editor;
e.loader.config({ paths: { vs: "/min/vs" } });
}),
jscript.init(),
]).then(() => {
p.render();
});
} }
return ( return (

View File

@ -1,70 +1,18 @@
import type { Editor as MonacoEditor } from "@monaco-editor/react";
import type { BuildOptions } from "esbuild-wasm";
import { FC } from "react"; import { FC } from "react";
import { useGlobal } from "web-utils"; import { useGlobal } from "web-utils";
import * as Y from "yjs"; import * as Y from "yjs";
import { jscript } from "../../../../utils/script/jscript";
import { Loading } from "../../../../utils/ui/loading"; import { Loading } from "../../../../utils/ui/loading";
import { Modal } from "../../../../utils/ui/modal"; import { Modal } from "../../../../utils/ui/modal";
import { EditorGlobal } from "../../logic/global"; import { EditorGlobal } from "../../logic/global";
import { rebuildTree } from "../../logic/tree-logic"; import { rebuildTree } from "../../logic/tree-logic";
import { initJS } from "./monaco/init"; import { DefaultScript, ScriptMonacoElement } from "./monaco/monaco-el";
import { DefaultScript, FBuild, ScriptMonacoElement } from "./monaco/monaco-el";
export const jscript = {
editor: null as typeof MonacoEditor | null,
build: null as null | FBuild,
_init: false as false | Promise<void>,
ready: false,
_editor: false,
async init(render: () => void) {
if (this._init) await this._init;
if (!this._init) {
this._init = new Promise<void>(async (resolve) => {
const { sendIPC } = await import("./esbuild/ipc");
await initJS();
if (!this._editor) {
this._editor = true;
const e = await import("@monaco-editor/react");
jscript.editor = e.Editor;
e.loader.config({ paths: { vs: "/min/vs" } });
}
this.build = async (entry, src, files, verbose?: boolean) => {
const options: BuildOptions = {
entryPoints: [entry],
jsx: "transform",
bundle: true,
format: "cjs",
minify: true,
};
const res = await sendIPC({
command_: "build",
input_: { ...files, [entry]: src },
options_: options,
});
if (verbose && res.stderr_) {
console.log(res.stderr_);
}
if (res.outputFiles_) return res.outputFiles_[0].text;
return "";
};
await this.build("el.tsx", `return ""`);
render();
resolve();
});
}
},
};
export const EScriptElement: FC<{}> = ({}) => { export const EScriptElement: FC<{}> = ({}) => {
const p = useGlobal(EditorGlobal, "EDITOR"); const p = useGlobal(EditorGlobal, "EDITOR");
if (!jscript.editor) { if (!jscript.editor && jscript.pending) {
jscript.init(p.render); jscript.pending.then(() => p.render());
} }
if (!p.script.active) { if (!p.script.active) {

View File

@ -11,12 +11,12 @@ import { editComp, loadComponent } from "../../../logic/comp";
import { EditorGlobal, PG } from "../../../logic/global"; import { EditorGlobal, PG } from "../../../logic/global";
import { rebuildTree } from "../../../logic/tree-logic"; import { rebuildTree } from "../../../logic/tree-logic";
import { newMap } from "../../../tools/yjs-tools"; import { newMap } from "../../../tools/yjs-tools";
import { jscript } from "../../script/script-element";
import { CPJsx } from "./CPJsx"; import { CPJsx } from "./CPJsx";
import { CPOption } from "./CPOption"; import { CPOption } from "./CPOption";
import { CPText } from "./CPText"; import { CPText } from "./CPText";
import { mergeScopeUpwards } from "../../../logic/tree-scope"; import { mergeScopeUpwards } from "../../../logic/tree-scope";
import { treePropEval } from "../../../logic/tree-prop"; import { treePropEval } from "../../../logic/tree-prop";
import { jscript } from "../../../../../utils/script/jscript";
export const CPInstance: FC<{ mitem: MItem }> = ({ mitem }) => { export const CPInstance: FC<{ mitem: MItem }> = ({ mitem }) => {
const p = useGlobal(EditorGlobal, "EDITOR"); const p = useGlobal(EditorGlobal, "EDITOR");
@ -65,10 +65,7 @@ export const CPInstance: FC<{ mitem: MItem }> = ({ mitem }) => {
} }
if (!jscript.build) { if (!jscript.build) {
jscript.init().then(() => { jscript.init(p.render);
local.status = "ready";
local.render();
});
} else { } else {
local.status = "ready"; local.status = "ready";
local.render(); local.render();

View File

@ -15,9 +15,9 @@ import { EditorGlobal, NodeMeta } from "../../../logic/global";
import { fillID } from "../../../tools/fill-id"; import { fillID } from "../../../tools/fill-id";
import { flatTree } from "../../../tools/flat-tree"; import { flatTree } from "../../../tools/flat-tree";
import { newMap } from "../../../tools/yjs-tools"; import { newMap } from "../../../tools/yjs-tools";
import { jscript } from "../../script/script-element";
import { detachComp } from "./action/detach"; import { detachComp } from "./action/detach";
import { rebuildTree } from "../../../logic/tree-logic"; import { rebuildTree } from "../../../logic/tree-logic";
import { jscript } from "../../../../../utils/script/jscript";
export const ETreeRightClick: FC<{ export const ETreeRightClick: FC<{
node: NodeModel<NodeMeta>; node: NodeModel<NodeMeta>;
@ -304,7 +304,7 @@ export const ETreeRightClick: FC<{
label="Detach" label="Detach"
onClick={async () => { onClick={async () => {
if (!jscript.build) { if (!jscript.build) {
await jscript.init(); await jscript.init(p.render);
} }
if (jscript.build && p.treeMeta[item.id]) { if (jscript.build && p.treeMeta[item.id]) {
detachComp( detachComp(
@ -344,7 +344,7 @@ export const ETreeRightClick: FC<{
comp_id: rootComp ? rootComp.id : undefined, comp_id: rootComp ? rootComp.id : undefined,
group_id, group_id,
}) })
.then(async (e) => { .then(async (e: any) => {
if (e) { if (e) {
await loadComponent(p, e.id); await loadComponent(p, e.id);
delete p.compLoading[item.id]; delete p.compLoading[item.id];

View File

@ -0,0 +1,85 @@
import type { Editor as MonacoEditor } from "@monaco-editor/react";
import type { BuildOptions } from "esbuild-wasm";
import type Prettier from "prettier/standalone";
import type estree from "prettier/plugins/estree";
import type ts from "prettier/plugins/typescript";
export type FBuild = (
entryFileName: string,
src: string,
files?: Record<string, string>,
verbose?: boolean
) => Promise<string>;
export const initJS = async () => {
const { tryToSetCurrentVersion } = await import("./esbuild/versions");
await tryToSetCurrentVersion("latest");
};
export const jscript = {
editor: null as typeof MonacoEditor | null,
build: null as null | FBuild,
pending: null as null | Promise<void>,
events: {
editorLoaded: () => {},
esbuildLoaded: () => {},
prettierLoaded: () => {},
pendingDone: () => {},
},
prettier: {
standalone: null as null | typeof Prettier,
estree: null as null | typeof estree,
ts: null as null | typeof ts,
},
async init(render: () => void) {
if (this.pending) {
await this.pending;
render();
}
if (!this.pending) {
this.pending = new Promise<void>(async (resolve) => {
this.events.pendingDone = resolve;
const { sendIPC } = await import("./esbuild/ipc");
await initJS();
this.events.esbuildLoaded();
this.prettier.standalone = (
await import("prettier/standalone")
).default;
this.prettier.estree = await import("prettier/plugins/estree");
this.prettier.ts = await import("prettier/plugins/typescript");
this.events.prettierLoaded();
const e = await import("@monaco-editor/react");
jscript.editor = e.Editor;
e.loader.config({ paths: { vs: "/min/vs" } });
this.events.editorLoaded();
this.build = async (entry, src, files, verbose?: boolean) => {
const options: BuildOptions = {
entryPoints: [entry],
jsx: "transform",
bundle: true,
format: "cjs",
minify: true,
};
const res = await sendIPC({
command_: "build",
input_: { ...files, [entry]: src },
options_: options,
});
if (verbose && res.stderr_) {
console.log(res.stderr_);
}
if (res.outputFiles_) return res.outputFiles_[0].text;
return "";
};
await this.build("el.tsx", `return ""`);
render();
});
}
},
};

View File

@ -0,0 +1,152 @@
import type { OnMount } from "@monaco-editor/react";
import trim from "lodash.trim";
import {
MonacoJsxSyntaxHighlight,
getWorker,
} from "monaco-jsx-syntax-highlight-v2";
import { jscript } from "./jscript";
export type MonacoEditor = Parameters<OnMount>[0];
type Monaco = Parameters<OnMount>[1];
type CompilerOptions = Parameters<
Parameters<OnMount>[1]["languages"]["typescript"]["typescriptDefaults"]["setCompilerOptions"]
>[0];
export const jsMount = async (editor: MonacoEditor, monaco: Monaco) => {
const m = monaco as any;
if (!m.customJSMounted) {
m.customJSMounted = true;
} else {
return;
}
const compilerOptions: CompilerOptions = {
jsx: monaco.languages.typescript.JsxEmit.React,
jsxFactory: "React.createElement",
jsxFragmentFactory: "React.Fragment",
target: monaco.languages.typescript.ScriptTarget.ES2015,
allowNonTsExtensions: true,
lib: ["esnext", "dom"],
module: monaco.languages.typescript.ModuleKind.ESNext,
esModuleInterop: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
};
const jsxHgController = new MonacoJsxSyntaxHighlight(getWorker(), monaco);
const { highlighter } = jsxHgController.highlighterBuilder({
editor: editor,
});
highlighter();
editor.onDidChangeModelContent(() => {
highlighter();
});
monaco.languages.registerDocumentFormattingEditProvider("typescript", {
async provideDocumentFormattingEdits(model, options, token) {
const prettier = jscript.prettier.standalone;
const prettier_ts = jscript.prettier.ts;
const prettier_estree = jscript.prettier.estree;
if (prettier && prettier_estree && prettier_ts) {
const text = trim(
await prettier.format(model.getValue(), {
parser: "typescript",
plugins: [prettier_ts, prettier_estree],
}),
"; \n"
);
return [
{
range: model.getFullModelRange(),
text,
},
];
}
},
});
monaco.languages.registerCompletionItemProvider("typescript", {
provideCompletionItems: (model, position) => {
const word = model.getWordUntilPosition(position);
return {
suggestions: [
{
label: "log",
kind: monaco.languages.CompletionItemKind.Snippet,
documentation: "Add Console.log",
insertText: `console.log($1)`,
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
},
},
{
label: "sfy",
kind: monaco.languages.CompletionItemKind.Snippet,
documentation: "Add JSON.stringify",
insertText: `JSON.stringify($1)`,
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
},
},
],
};
},
});
monaco.languages.registerCompletionItemProvider("typescript", {
triggerCharacters: [">"],
provideCompletionItems: (model, position) => {
const codePre: string = model.getValueInRange({
startLineNumber: position.lineNumber,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column,
});
const tag = codePre.match(/.*<(\w+)>$/)?.[1];
if (!tag) {
return;
}
const word = model.getWordUntilPosition(position);
return {
suggestions: [
{
label: `</${tag}>`,
kind: monaco.languages.CompletionItemKind.EnumMember,
insertText: `$1</${tag}>`,
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
},
},
],
};
},
});
monaco.languages.typescript.typescriptDefaults.setCompilerOptions(
compilerOptions
);
setTimeout(() => {
editor.getAction("editor.action.formatDocument")?.run();
}, 100);
};

View File

@ -0,0 +1,74 @@
export const baseTypings = `
type FC<T> = React.FC<T>;
const Fragment = React.Fragment;
const ReactNode = React.ReactNode;
const useCallback = React.useCallback;
const useMemo = React.useMemo;
const ReactElement = React.ReactElement;
const isValidElement = React.isValidElement;
const useEffect = React.useEffect;
const useState = React.useState;
const pathname: string;
const isEditor: boolean;
const isLayout: boolean;
const isMobile: boolean;
const isDesktop: boolean;
const preload: (pathname: string) => void;
const apiHeaders: Record<string, any>;
const navigate: (url:string) => void;
const params: any;
const cx = (...classNames: any[]) => string;
const css = (
tag: CSSAttribute | TemplateStringsArray | string,
...props: Array<string | number | boolean | undefined | null>
) => string;
const props: {
className: string;
onPointerDown?: () => void;
onPointerMove?: () => void;
onPointerLeave?: () => void;
};
const children: ReactNode;
const PassProp: FC<Record<string,any> & {children: React.ReactNode; }>;
const PassChild: FC<{name: string}>;
const Preload: FC<{url: string[]}>;
const apiurl: string;
const pageid: string;
type ITEM = {
id: string
name: string;
type: 'item' | 'text';
adv?: {
js?: string;
jsBuilt?: string;
css?: string;
html?: string;
},
text: string,
html: string,
component?: { id:string, props: Record<string, {
value: string,
valueBuilt: string,
meta: { type: string }
}>},
childs: ITEM[]
}
const newElement: (gen?: (item: ITEM) => ITEM | ITEM[]) => React.ReactNode;
const Local: <T extends Record<string, any>>(arg: {
name: string;
value: T;
children: ((local: T & { render: () => void }) => any);
deps?: any[];
effect?: (
local: T & { render: () => void }
) => void | (() => void) | Promise<void | (() => void)>;
hook?: (
local: T & { render: () => void }
) => void | (() => void) | Promise<void | (() => void)>;
cache?: boolean;
}) => ReactNode;
`;

View File

@ -0,0 +1,114 @@
import trim from "lodash.trim";
import { isValidElement } from "react";
export const extractProp = (prop: {
values: Record<string, any>;
types: Record<string, string>;
}) => {
const propTypes: string[] = [];
const props: Record<string, { val?: any; type?: string }> = {};
if (prop) {
if (prop.values) {
for (const [k, v] of Object.entries(prop.values)) {
if (!props[k]) {
props[k] = {};
}
if (typeof v === "function") {
if (isFunctionalComponent(v)) {
props[k].type = "React.FC";
} else if (isClassComponent(v)) {
props[k].type = "React.Component";
} else {
props[k].type = "any";
}
} else if (v) {
if (typeof v === "object" && v._jsx) {
props[k].type = "React.ReactElement;";
} else if (!!v.render && typeof v.$$typeof === "symbol") {
props[k].type = "React.FC<Record<string,any> & {ref?:any}>";
} else {
props[k].val = v;
}
}
}
}
}
if (prop.types) {
for (const [k, v] of Object.entries(prop.types)) {
if (!props[k]) {
props[k] = {};
}
props[k].type = v;
}
}
for (const [k, v] of Object.entries(props)) {
if (v.type) {
propTypes.push(`const ${k}: ${trim(v.type, "; \n")};`);
} else if (v.val) {
if (typeof v.val === "object" && isValidElement(v.val)) {
propTypes.push(`const ${k}: ReactElement;`);
} else {
try {
let val = v.val;
if (typeof val === "object") {
if (typeof val.render === "function") {
val = { ...val, render: () => {} };
}
propTypes.push(`const ${k}: ${recurseTypes(val)};`);
} else {
propTypes.push(`const ${k}: string;`);
}
} catch (e) {}
}
}
}
return propTypes;
};
function recurseTypes(object: any) {
const result: string[] = [];
if (typeof object === "object") {
if (object === null) return "null";
if (Array.isArray(object)) {
return `any[]`;
}
for (const [k, v] of Object.entries(object)) {
result.push(
`${k}: ${typeof v === "object" && v ? recurseTypes(v) : typeof v}`
);
}
return `{
${result.join(";\n ")}
}`;
}
return typeof object;
}
function isFunctionalComponent(Component: any) {
return (
typeof Component === "function" && // can be various things
!(
(
Component.prototype && // native arrows don't have prototypes
Component.prototype.isReactComponent
) // special property
)
);
}
function isClassComponent(Component: any) {
return !!(
typeof Component === "function" &&
Component.prototype &&
Component.prototype.isReactComponent
);
}

View File

@ -0,0 +1,14 @@
export const typeStringify = function (this: any, key: string, value: any) {
if (typeof value === "function") {
return `___FFF||any||FFF___`;
}
return value;
};
export const typeReviver = (key: any, value: any) => {
if (typeof key === "string" && key.indexOf("function ") === 0) {
let functionTemplate = `(${value})`;
return eval(functionTemplate);
}
return value;
};

View File

@ -0,0 +1,155 @@
import type { OnMount } from "@monaco-editor/react";
import { w } from "../types/general";
import { baseTypings } from "./types/base";
import { extractProp } from "./types/prop";
export type MonacoEditor = Parameters<OnMount>[0];
type Monaco = Parameters<OnMount>[1];
const map = new WeakMap<any>();
export const monacoTypings = async (
p: {
site_dts: string;
site: { api_url: string };
script: { siteTypes: Record<string, string> };
},
monaco: Monaco,
prop: { values: Record<string, any>; types: Record<string, string> }
) => {
if (!map.has(prop.values)) {
map.set(prop.values, true);
} else {
return;
}
if (w.prasiApi[p.site.api_url] && w.prasiApi[p.site.api_url].prismaTypes) {
const prisma = w.prasiApi[p.site.api_url].prismaTypes;
register(
monaco,
`\
declare module "ts:runtime/index" {
${prisma["runtime/index.d.ts"]}
}`,
`ts:runtime/index.d.ts`
);
register(
monaco,
`\
declare module "ts:runtime/library" {
${prisma["runtime/library.d.ts"]}
}`,
`ts:runtime/library.d.ts`
);
register(
monaco,
`\
declare module "ts:prisma" {
${prisma["prisma.d.ts"].replace(
`import * as runtime from './runtime/library';`,
`import * as runtime from 'ts:runtime/library';`
)}
}`,
`ts:prisma.d.ts`
);
register(monaco, w.prasiApi[p.site.api_url].apiTypes, "ts:api.d.ts");
}
monaco.languages.typescript.typescriptDefaults.setExtraLibs([
{
filePath: "react.d.ts",
content: await loadText(
"https://cdn.jsdelivr.net/npm/@types/react@18.2.0/index.d.ts"
),
},
{
filePath: "jsx-runtime.d.ts",
content: await loadText(
"https://cdn.jsdelivr.net/npm/@types/react@18.2.0/jsx-runtime.d.ts"
),
},
{
filePath: "site.d.ts",
content: p.site_dts.replaceAll("export declare const", "declare const"),
},
]);
const propText = extractProp({
values: prop.values,
types: { ...prop.types, ...p.script.siteTypes },
});
const apiTypes = w.prasiApi[p.site.api_url]
? w.prasiApi[p.site.api_url].apiTypes
: "";
let apiPath = "app/gen/srv/api/srv";
if (apiTypes && apiTypes.includes(`export * as srv from "gen/srv/api/srv"`)) {
apiPath = "gen/srv/api/srv";
}
register(
monaco,
`\
import React from 'react';
import prisma from 'ts:prisma';
${iftext(
apiTypes,
`\
import "./api"
import type * as SRVAPI from "${apiPath}";`
)}
declare global {;
const db: prisma.PrismaClient;
${baseTypings}
const moko: {nama: string};
${propText.join("\n")}
${iftext(
apiTypes,
`
type Api = typeof SRVAPI;
type ApiName = keyof Api;
const api: { [k in ApiName]: Awaited<Api[k]["handler"]>["_"]["api"] };
`
)}
}
`,
"ts:global.d.ts"
);
};
const loadText = async (url: string) => {
try {
const res = await fetch(url);
return await res.text();
} catch (e) {
return "";
}
};
export const iftext = (condition: any, text: string) => {
if (condition) {
return text;
}
return "";
};
export const register = (monaco: Monaco, source: string, uri: string) => {
const model = monaco.editor.getModels().find((e) => {
return e.uri.toString() === uri;
});
if (model) {
model.setValue(source);
} else {
monaco.editor.createModel(source, "typescript", monaco.Uri.parse(uri));
}
};