mirror of
https://github.com/hansjone/mind-map.git
synced 2026-10-09 05:43:14 +08:00
feat: ship prebuilt plugin bundle for remote dsh install
Remote github installs only get packages/dsh-plugin; without bundle the host tried pnpm build and hung. Pack web+server into bundle/ and start from it so other machines can run after dsh plugin add. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d95f033d1f
commit
b23959400d
13 changed files with 28325 additions and 41 deletions
|
|
@ -1,15 +1,30 @@
|
|||
# dsh-mind-map 本地安装到 DSH web profile(开发用)
|
||||
# dsh-mind-map 安装
|
||||
|
||||
```powershell
|
||||
$plugin = "d:\project\chatgpt\mind-map\packages\dsh-plugin"
|
||||
# 用 dsh plugin add 链到本地包,或在 profile 里 link
|
||||
dsh plugin --profile web add -w $plugin
|
||||
## 远程安装(推荐)
|
||||
|
||||
仓库需包含预构建 `packages/dsh-plugin/bundle`(维护者执行 `pnpm pack:plugin` 后推送)。
|
||||
|
||||
```bash
|
||||
dsh plugin --profile web add -w "github:hansjone/mind-map#path:packages/dsh-plugin"
|
||||
```
|
||||
|
||||
装好后:
|
||||
装好后重启 `dsh web`,硬刷新浏览器。依赖:`dsh-better-sidebar`。
|
||||
|
||||
1. 重启 `dsh web`,浏览器硬刷新(Ctrl+Shift+R)
|
||||
2. 右侧栏「+」选 **思维导图**,或 设置 → Mind Map → **打开思维导图 Tab**
|
||||
3. 拓扑占满右侧内容区;对话继续用中间 DSH 输入框
|
||||
启动日志应出现:
|
||||
|
||||
依赖:已安装 `dsh-better-sidebar`(提供 `ctx.betterSidebar.registerTab`)。
|
||||
```text
|
||||
[dsh-mind-map] starting packaged bundle/server.mjs
|
||||
```
|
||||
|
||||
若仍出现 `building web UI…`,说明装到的版本没有 `bundle/`,请更新插件或改用本地完整仓库。
|
||||
|
||||
## 本地开发(link 整个 monorepo)
|
||||
|
||||
```powershell
|
||||
cd d:\project\chatgpt\mind-map
|
||||
pnpm install
|
||||
pnpm pack:plugin
|
||||
dsh plugin --profile web add -w "d:\project\chatgpt\mind-map\packages\dsh-plugin"
|
||||
```
|
||||
|
||||
开发改 UI 时可继续用 monorepo 源码启动(无 bundle 时会自动 `pnpm --filter @mind-map/web build`)。
|
||||
|
|
|
|||
1
packages/dsh-plugin/bundle/README.txt
Normal file
1
packages/dsh-plugin/bundle/README.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Prebuilt assets for remote dsh plugin install. Regenerated by: node packages/dsh-plugin/scripts/pack-bundle.mjs
|
||||
28067
packages/dsh-plugin/bundle/server.mjs
Normal file
28067
packages/dsh-plugin/bundle/server.mjs
Normal file
File diff suppressed because it is too large
Load diff
1
packages/dsh-plugin/bundle/web/assets/index-BK6FV7kG.css
Normal file
1
packages/dsh-plugin/bundle/web/assets/index-BK6FV7kG.css
Normal file
File diff suppressed because one or more lines are too long
51
packages/dsh-plugin/bundle/web/assets/index-iTIYRavW.js
Normal file
51
packages/dsh-plugin/bundle/web/assets/index-iTIYRavW.js
Normal file
File diff suppressed because one or more lines are too long
13
packages/dsh-plugin/bundle/web/index.html
Normal file
13
packages/dsh-plugin/bundle/web/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Mind Map</title>
|
||||
<script type="module" crossorigin src="/assets/index-iTIYRavW.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BK6FV7kG.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -2,6 +2,10 @@
|
|||
* Start / stop the mind-map Hono server as a child process.
|
||||
* Lifetime is tied to the DSH host: when the plugin disposes / process exits,
|
||||
* the canvas server is killed (including process tree on Windows).
|
||||
*
|
||||
* Two layouts:
|
||||
* - Packaged (remote `dsh plugin add`): packages/dsh-plugin/bundle/{web,server.mjs}
|
||||
* - Monorepo (local link): ../../.. mind-map root with apps/web + apps/server
|
||||
*/
|
||||
|
||||
import { spawn, execSync } from 'node:child_process'
|
||||
|
|
@ -11,7 +15,26 @@ import { fileURLToPath } from 'node:url'
|
|||
import { createRequire } from 'node:module'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
export const mindMapRoot = join(__dirname, '../../..')
|
||||
/** dsh-mind-map package root (…/dsh-mind-map or …/packages/dsh-plugin) */
|
||||
export const pluginRoot = join(__dirname, '..')
|
||||
/** Monorepo root when linked from source; otherwise same as pluginRoot when packaged. */
|
||||
export const mindMapRoot = (() => {
|
||||
const mono = join(__dirname, '../../..')
|
||||
if (existsSync(join(mono, 'apps', 'web', 'package.json'))) return mono
|
||||
return pluginRoot
|
||||
})()
|
||||
|
||||
function bundledServer() {
|
||||
return join(pluginRoot, 'bundle', 'server.mjs')
|
||||
}
|
||||
|
||||
function bundledWebIndex() {
|
||||
return join(pluginRoot, 'bundle', 'web', 'index.html')
|
||||
}
|
||||
|
||||
function isPackagedInstall() {
|
||||
return existsSync(bundledServer()) && existsSync(bundledWebIndex())
|
||||
}
|
||||
|
||||
function resolveTsxCliRel() {
|
||||
const candidates = [
|
||||
|
|
@ -33,6 +56,9 @@ function resolveTsxCliRel() {
|
|||
}
|
||||
|
||||
function ensureWebDist() {
|
||||
// Remote/plugin install ships prebuilt assets — never run pnpm here.
|
||||
if (isPackagedInstall()) return
|
||||
|
||||
const indexHtml = join(mindMapRoot, 'apps', 'web', 'dist', 'index.html')
|
||||
const watchSrc = [
|
||||
join(mindMapRoot, 'apps', 'web', 'src', 'TopologyCanvas.tsx'),
|
||||
|
|
@ -55,6 +81,16 @@ function ensureWebDist() {
|
|||
}
|
||||
}
|
||||
if (!needsBuild) return
|
||||
|
||||
if (!existsSync(join(mindMapRoot, 'pnpm-workspace.yaml'))) {
|
||||
throw new Error(
|
||||
'mind-map web UI is missing and this is not a monorepo checkout. ' +
|
||||
'Reinstall a release that includes packages/dsh-plugin/bundle ' +
|
||||
'(github:hansjone/mind-map#path:packages/dsh-plugin after maintainer runs pnpm pack:plugin), ' +
|
||||
'or clone the full repo and link locally.',
|
||||
)
|
||||
}
|
||||
|
||||
console.log('[dsh-mind-map] building web UI (apps/web/dist missing or stale)…')
|
||||
try {
|
||||
execSync('pnpm --filter @mind-map/web build', {
|
||||
|
|
@ -187,14 +223,7 @@ export function createRuntime() {
|
|||
await new Promise((r) => setTimeout(r, 400))
|
||||
}
|
||||
|
||||
const entryAbs = join(mindMapRoot, 'apps/server/src/index.ts')
|
||||
const distAbs = join(mindMapRoot, 'apps/server/dist/index.js')
|
||||
const useTs = existsSync(entryAbs)
|
||||
const scriptAbs = useTs ? entryAbs : distAbs
|
||||
if (!existsSync(scriptAbs)) {
|
||||
throw new Error(`mind-map server entry not found under ${mindMapRoot}`)
|
||||
}
|
||||
|
||||
const packaged = isPackagedInstall()
|
||||
const env = {
|
||||
...process.env,
|
||||
PORT: String(port),
|
||||
|
|
@ -205,18 +234,35 @@ export function createRuntime() {
|
|||
}
|
||||
if (dataDir) env.DATA_DIR = dataDir
|
||||
|
||||
const scriptRel = useTs ? 'apps/server/src/index.ts' : 'apps/server/dist/index.js'
|
||||
let cwd
|
||||
let args
|
||||
if (useTs) {
|
||||
const tsxCli = resolveTsxCliRel()
|
||||
if (!tsxCli) throw new Error('tsx not found; run pnpm install in mind-map')
|
||||
args = [tsxCli, scriptRel]
|
||||
if (packaged) {
|
||||
cwd = pluginRoot
|
||||
env.MINDMAP_ROOT = pluginRoot
|
||||
env.MINDMAP_WEB_DIST = join(pluginRoot, 'bundle', 'web')
|
||||
args = [bundledServer()]
|
||||
console.log('[dsh-mind-map] starting packaged bundle/server.mjs')
|
||||
} else {
|
||||
args = [scriptRel]
|
||||
cwd = mindMapRoot
|
||||
const entryAbs = join(mindMapRoot, 'apps/server/src/index.ts')
|
||||
const distAbs = join(mindMapRoot, 'apps/server/dist/index.js')
|
||||
const useTs = existsSync(entryAbs)
|
||||
const scriptAbs = useTs ? entryAbs : distAbs
|
||||
if (!existsSync(scriptAbs)) {
|
||||
throw new Error(`mind-map server entry not found under ${mindMapRoot}`)
|
||||
}
|
||||
const scriptRel = useTs ? 'apps/server/src/index.ts' : 'apps/server/dist/index.js'
|
||||
if (useTs) {
|
||||
const tsxCli = resolveTsxCliRel()
|
||||
if (!tsxCli) throw new Error('tsx not found; run pnpm install in mind-map')
|
||||
args = [tsxCli, scriptRel]
|
||||
} else {
|
||||
args = [scriptRel]
|
||||
}
|
||||
}
|
||||
|
||||
child = spawn(process.execPath, args, {
|
||||
cwd: mindMapRoot,
|
||||
cwd,
|
||||
env,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
windowsHide: true,
|
||||
|
|
@ -277,8 +323,9 @@ export function createRuntime() {
|
|||
...last,
|
||||
running: Boolean(child && !child.killed),
|
||||
pid: child?.pid || null,
|
||||
packaged: isPackagedInstall(),
|
||||
}
|
||||
}
|
||||
|
||||
return { start, stop, status, mindMapRoot }
|
||||
return { start, stop, status, mindMapRoot, pluginRoot }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,14 @@
|
|||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"bundle",
|
||||
"cordis.patch.yml",
|
||||
"README.md"
|
||||
"README.md",
|
||||
"INSTALL.md"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "node --test test/*.test.js"
|
||||
"test": "node --test test/*.test.js",
|
||||
"pack": "node scripts/pack-bundle.mjs"
|
||||
},
|
||||
"dsh": {
|
||||
"bundle": {
|
||||
|
|
|
|||
71
packages/dsh-plugin/scripts/pack-bundle.mjs
Normal file
71
packages/dsh-plugin/scripts/pack-bundle.mjs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Pack prebuilt web UI + server into packages/dsh-plugin/bundle
|
||||
* so `dsh plugin add github:…#path:packages/dsh-plugin` works without monorepo.
|
||||
*/
|
||||
import { execSync } from "node:child_process";
|
||||
import {
|
||||
cpSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import * as esbuild from "esbuild";
|
||||
|
||||
const pluginRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
||||
const repoRoot = join(pluginRoot, "../..");
|
||||
const bundleRoot = join(pluginRoot, "bundle");
|
||||
const webOut = join(bundleRoot, "web");
|
||||
const serverOut = join(bundleRoot, "server.mjs");
|
||||
|
||||
function run(cmd, cwd = repoRoot) {
|
||||
console.log(`[pack] ${cmd}`);
|
||||
execSync(cmd, { cwd, stdio: "inherit", env: process.env, windowsHide: true });
|
||||
}
|
||||
|
||||
// 1) Build workspace libs + web
|
||||
run("pnpm --filter @mind-map/shared build");
|
||||
run("pnpm --filter @mind-map/graph-core build");
|
||||
run("pnpm --filter @mind-map/graph-store build");
|
||||
run("pnpm --filter @mind-map/layout-engine build");
|
||||
// dream-skin ships TS+JSON; esbuild resolves it without a separate tsc emit
|
||||
run("pnpm --filter @mind-map/web build");
|
||||
|
||||
const webDist = join(repoRoot, "apps/web/dist");
|
||||
if (!existsSync(join(webDist, "index.html"))) {
|
||||
throw new Error("apps/web/dist/index.html missing after build");
|
||||
}
|
||||
|
||||
// 2) Fresh bundle dir
|
||||
rmSync(bundleRoot, { recursive: true, force: true });
|
||||
mkdirSync(webOut, { recursive: true });
|
||||
cpSync(webDist, webOut, { recursive: true });
|
||||
|
||||
// 3) Bundle server (workspace + npm deps inlined; node builtins stay external)
|
||||
console.log("[pack] esbuild apps/server → bundle/server.mjs");
|
||||
await esbuild.build({
|
||||
entryPoints: [join(repoRoot, "apps/server/src/index.ts")],
|
||||
bundle: true,
|
||||
platform: "node",
|
||||
format: "esm",
|
||||
outfile: serverOut,
|
||||
packages: "bundle",
|
||||
banner: {
|
||||
js: "import { createRequire as __mmCr } from 'node:module'; const require = __mmCr(import.meta.url);",
|
||||
},
|
||||
logLevel: "info",
|
||||
});
|
||||
|
||||
if (!existsSync(serverOut)) {
|
||||
throw new Error("bundle/server.mjs was not produced");
|
||||
}
|
||||
|
||||
writeFileSync(
|
||||
join(bundleRoot, "README.txt"),
|
||||
"Prebuilt assets for remote dsh plugin install. Regenerated by: node packages/dsh-plugin/scripts/pack-bundle.mjs\n",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
console.log("[pack] OK →", bundleRoot);
|
||||
Loading…
Add table
Add a link
Reference in a new issue