From a0d0ca0ca9959e41b3bcf2a649ab6a94022ad1fc Mon Sep 17 00:00:00 2001 From: codetypes Date: Sat, 25 Jan 2025 21:55:00 +0800 Subject: [PATCH] build project in command line --- electron/main/index.ts | 31 ++++++++++++++++++++++---- src/behavior3 | 2 +- src/components/menu.tsx | 49 ++++++++++++++++++++++++++++++++++++++++- src/misc/path.ts | 18 +++++++++++++++ src/misc/template.ts | 21 +++++++++++++----- 5 files changed, 110 insertions(+), 11 deletions(-) diff --git a/electron/main/index.ts b/electron/main/index.ts index 7222992..965fdbd 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -1,9 +1,7 @@ -import { app, BrowserWindow, shell, ipcMain } from "electron"; +import { app, BrowserWindow, ipcMain, shell } from "electron"; import { release } from "node:os"; import path, { join } from "node:path"; -import { update } from "./update"; - -import * as fs from "fs"; +import { argv } from "node:process"; // The built directory structure // @@ -47,6 +45,20 @@ const url = process.env.VITE_DEV_SERVER_URL; const indexHtml = join(process.env.DIST, "index.html"); const windows: Workspace[] = []; +let buildProject: string; +let buildOutput: string; + +for (let i = 0; i < argv.length; i++) { + const arg = argv[i]; + if (arg === "--project") { + buildProject = argv[i + 1]; + i++; + } else if (arg === "--build") { + buildOutput = argv[i + 1]; + i++; + } +} + async function createWindow(projectPath?: string) { const win = new BrowserWindow({ title: "Behaviour3 Editor", @@ -98,6 +110,10 @@ async function createWindow(projectPath?: string) { win?.webContents.send("open-project", workspace.projectPath); } + if (buildProject && buildOutput) { + win?.webContents.send("build-project", buildProject, buildOutput); + } + const nextWin = BrowserWindow.getAllWindows().at(-1); if (nextWin) { nextWin.focus(); @@ -110,6 +126,13 @@ async function createWindow(projectPath?: string) { win.on("closed", () => { const index = windows.findIndex((w) => w.window === win); windows.splice(index, 1); + + if (buildOutput && buildProject && windows.length === 0) { + app.exit(0); + } else { + buildOutput = undefined; + buildProject = undefined; + } }); // Make all links open with the browser, not with the application diff --git a/src/behavior3 b/src/behavior3 index 98117af..5d187cb 160000 --- a/src/behavior3 +++ b/src/behavior3 @@ -1 +1 @@ -Subproject commit 98117afec4a5fe7ef4bebc8443b5439714fb7f7e +Subproject commit 5d187cb681306cea65e44ca17224f4dc43353482 diff --git a/src/components/menu.tsx b/src/components/menu.tsx index b8b487d..0298429 100644 --- a/src/components/menu.tsx +++ b/src/components/menu.tsx @@ -3,8 +3,16 @@ import { useWorkspace } from "@/contexts/workspace-context"; import * as b3util from "@/misc/b3util"; import i18n from "@/misc/i18n"; import { Hotkey, isMacos, sendInputEvent } from "@/misc/keys"; +import Path from "@/misc/path"; import { CheckOutlined } from "@ant-design/icons"; -import { Menu as AppMenu, BrowserWindow, app, dialog } from "@electron/remote"; +import { + Menu as AppMenu, + BrowserWindow, + app, + dialog, + getCurrentWindow, + getGlobal, +} from "@electron/remote"; import { Button, DropDownProps, Dropdown, Flex, FlexProps, LayoutProps, Space } from "antd"; import { ItemType } from "antd/es/menu/interface"; import { ipcRenderer } from "electron"; @@ -37,6 +45,45 @@ ipcRenderer.on("refresh-app-men", () => { useSetting.getState().load(); }); +ipcRenderer.on("build-project", (_, project, buildDir) => { + const mainConsole = getGlobal("console") as Console; + try { + let hasError = false; + project = Path.posixPath(project); + buildDir = Path.posixPath(buildDir); + if (!project.endsWith(".b3-workspace")) { + throw new Error(`'${project}' is not a workspace`); + } + const workspace = useWorkspace.getState(); + workspace.workdir = Path.dirname(project); + workspace.loadNodeDefs(); + for (const path of Path.ls(Path.dirname(project), true)) { + if (path.endsWith(".json")) { + const buildpath = buildDir + "/" + path.substring(workspace.workdir.length + 1); + const treeModel = b3util.createBuildData(path); + if (treeModel && treeModel.export === false) { + mainConsole.log("skip:", buildpath); + continue; + } + mainConsole.log("build:", buildpath); + if (!b3util.checkNodeData(treeModel?.root)) { + hasError = true; + } + fs.mkdirSync(Path.dirname(buildpath), { recursive: true }); + fs.writeFileSync(buildpath, JSON.stringify(treeModel, null, 2)); + } + } + if (hasError) { + mainConsole.error(i18n.t("buildFailed")); + } else { + mainConsole.log(i18n.t("buildCompleted")); + } + } catch (error) { + mainConsole.error(i18n.t("buildFailed")); + } + getCurrentWindow().close(); +}); + export const Menu: FC = () => { const { t } = useTranslation(); const [trigger, setTrigger] = useState(["click"]); diff --git a/src/misc/path.ts b/src/misc/path.ts index c9b329f..01fce85 100644 --- a/src/misc/path.ts +++ b/src/misc/path.ts @@ -1,3 +1,4 @@ +import * as fs from "fs"; import path from "path"; const Path = path; @@ -6,6 +7,7 @@ declare module "path" { interface PlatformPath { basenameWithoutExt(path: string): string; posixPath(path: string): string; + ls(path: string, recursive?: boolean): string[]; } } @@ -17,4 +19,20 @@ path.posixPath = (str: string) => { return path.normalize(str).replace(/\\/g, "/"); }; +const readdir = (dir: string, recursive: boolean, callback: (file: string) => void) => { + fs.readdirSync(dir).forEach((file) => { + file = Path.posixPath(dir + "/" + file); + callback(file); + if (recursive && fs.statSync(file).isDirectory()) { + readdir(file, recursive, callback); + } + }); +}; + +path.ls = (dir: string, recursive?: boolean) => { + const paths: string[] = []; + readdir(dir, recursive ?? false, (file) => paths.push(file)); + return paths.sort(); +}; + export default Path; diff --git a/src/misc/template.ts b/src/misc/template.ts index 6b4b573..536d680 100644 --- a/src/misc/template.ts +++ b/src/misc/template.ts @@ -1,10 +1,21 @@ -import { Context, NodeDef } from "@/behavior3/src/behavior3"; +import { Context, Node, NodeDef } from "@/behavior3/src/behavior3"; +import { DeepReadonly } from "../behavior3/src/behavior3/context"; + +class NodeContext extends Context { + loadTree(path: string): Promise { + throw new Error("Method not implemented."); + } +} export const zhNodeDef = () => { - const context = new Context(); - const defs: NodeDef[] = []; - for (const v of context.processResolvers.values()) { - const descriptor = v.descriptor; + const context = new (class extends Context { + loadTree(path: string): Promise { + throw new Error("Method not implemented."); + } + })(); + const defs: DeepReadonly[] = []; + for (const k in context.nodeDefs) { + const descriptor = context.nodeDefs[k]; defs.push(descriptor); } defs.sort((a, b) => a.name.localeCompare(b.name));