@dimina-kit/devkit 0.1.2-dev.20260701155140 → 0.1.2-dev.20260702145840

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,7 +63,7 @@ await session.close()
63
63
 
64
64
  ## 工作流程
65
65
 
66
- 1. 在 fork 出的长驻编译子进程中调用 `@dimina/compiler` 编译小程序项目(cwd 隔离:`chdir` 只发生在子进程内,宿主进程 cwd 不被污染;编译器崩溃不会拖垮宿主,下次重编译自动重新 fork 恢复)
66
+ 1. 在 fork 出的长驻编译子进程中调用 `@dimina-kit/compiler/pool-node`(dmcc 兼容的常驻编译池)编译小程序项目(cwd 隔离:`chdir` 只发生在子进程内,宿主进程 cwd 不被污染;编译器崩溃不会拖垮宿主,下次重编译自动重新 fork 恢复)
67
67
  2. 子进程的 stdout/stderr 按行管道回父进程,经 `filterDmccLogLine` 过滤噪音后通过 `onLog` 回调送出
68
68
  3. 启动基于 Express 的 H5 容器预览服务器(含 CORS、SPA fallback、可选 live-reload)
69
69
  4. 通过 chokidar 监听项目目录变更,自动触发增量编译和页面刷新;编译进行中的变更不会丢失,会在当前编译结束后合并为恰好一次尾随重编译(trailing rebuild)
@@ -21,8 +21,12 @@ export interface WorkerResultMessage {
21
21
  };
22
22
  }
23
23
  export interface CompileWorkerHandlerDeps {
24
- /** Lazy `require('@dimina/compiler')` — deferred to the first build. */
25
- loadCompiler: () => WorkerBuildFn;
24
+ /**
25
+ * Lazy compiler load — deferred to the first build. May be sync (a plain
26
+ * `require`) or async (a dynamic `import()` of the ESM `@dimina-kit/compiler`
27
+ * pool); the handler awaits it either way. Loaded ONCE and cached.
28
+ */
29
+ loadCompiler: () => WorkerBuildFn | Promise<WorkerBuildFn>;
26
30
  /** `process.chdir` — mutates the CHILD process cwd only. */
27
31
  chdir: (dir: string) => void;
28
32
  /** `process.send` — replies to the parent. */
@@ -1 +1 @@
1
- {"version":3,"file":"compile-worker-entry.d.ts","sourceRoot":"","sources":["../src/compile-worker-entry.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;CAC5E;AAED,MAAM,MAAM,aAAa,GAAG,CAC3B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,OAAO,EACpB,OAAO,EAAE,kBAAkB,KACvB,OAAO,CAAC,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;AAE9C,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,aAAa,GAAG,IAAI,CAAA;IAC7B,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACxC,wEAAwE;IACxE,YAAY,EAAE,MAAM,aAAa,CAAA;IACjC,4DAA4D;IAC5D,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,8CAA8C;IAC9C,IAAI,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAA;CACxC;AAiBD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACzC,IAAI,EAAE,wBAAwB,GAC5B,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAwBjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE;IACL,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,KAAK,OAAO,CAAA;IACxF,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B,EACD,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GACtC,IAAI,CASN"}
1
+ {"version":3,"file":"compile-worker-entry.d.ts","sourceRoot":"","sources":["../src/compile-worker-entry.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;CAC5E;AAED,MAAM,MAAM,aAAa,GAAG,CAC3B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,OAAO,EACpB,OAAO,EAAE,kBAAkB,KACvB,OAAO,CAAC,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;AAE9C,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,aAAa,GAAG,IAAI,CAAA;IAC7B,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACxC;;;;OAIG;IACH,YAAY,EAAE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC1D,4DAA4D;IAC5D,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,8CAA8C;IAC9C,IAAI,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAA;CACxC;AAiBD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACzC,IAAI,EAAE,wBAAwB,GAC5B,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAwBjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE;IACL,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,KAAK,OAAO,CAAA;IACxF,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B,EACD,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GACtC,IAAI,CASN"}
@@ -12,7 +12,6 @@
12
12
  * in `createCompileWorkerHandler` (dependency-injected) so it is unit-testable
13
13
  * without forking.
14
14
  */
15
- import { createRequire } from 'node:module';
16
15
  import path from 'node:path';
17
16
  import { fileURLToPath } from 'node:url';
18
17
  function isBuildMessage(msg) {
@@ -35,7 +34,7 @@ export function createCompileWorkerHandler(deps) {
35
34
  return;
36
35
  try {
37
36
  if (!cachedBuild)
38
- cachedBuild = deps.loadCompiler();
37
+ cachedBuild = await deps.loadCompiler();
39
38
  deps.chdir(msg.projectPath);
40
39
  const appInfo = await cachedBuild(msg.outputDir, msg.projectPath, true, msg.options);
41
40
  deps.send({ type: 'result', appInfo: appInfo ?? null });
@@ -102,9 +101,12 @@ const isForkedWorkerEntry = typeof process.argv[1] === 'string'
102
101
  && path.resolve(stripModuleQuery(process.argv[1])) === selfModulePath
103
102
  && typeof process.send === 'function';
104
103
  if (isForkedWorkerEntry) {
105
- const require = createRequire(import.meta.url);
106
104
  const handler = createCompileWorkerHandler({
107
- loadCompiler: () => require('@dimina/compiler'),
105
+ // Load @dimina-kit/compiler's resident Node worker_threads pool (drop-in for
106
+ // dmcc's build(): same signature, same log/error surface, but keeps its 3 stage
107
+ // workers WARM across rebuilds). ESM package → dynamic import (works on Node 20+);
108
+ // its default export is the pooled build fn.
109
+ loadCompiler: async () => (await import('@dimina-kit/compiler/pool-node')).default,
108
110
  chdir: dir => process.chdir(dir),
109
111
  send: (msg) => {
110
112
  process.send?.(msg);
@@ -56,6 +56,14 @@ function workerEntryPath() {
56
56
  return js;
57
57
  return path.join(__dirname, 'compile-worker-entry.ts');
58
58
  }
59
+ /** execArgv for the entry — same rule as compile-worker.ts (a `.ts` entry needs
60
+ * type stripping, default only since Node 22.18; like the suite itself this
61
+ * assumes Node ≥22.6, where the explicit flag exists). */
62
+ function workerExecArgv(entry) {
63
+ return entry.endsWith('.ts')
64
+ ? ['--experimental-strip-types', '--disable-warning=ExperimentalWarning']
65
+ : [];
66
+ }
59
67
  function pidAlive(pid) {
60
68
  try {
61
69
  process.kill(pid, 0);
@@ -147,7 +155,8 @@ function makeFixture() {
147
155
  }
148
156
  describe('① orphan safety net — the worker kills ITSELF when the IPC channel dies', () => {
149
157
  it('a forked entry whose parent disconnects the IPC channel exits ON ITS OWN with code 0 (no kill from anyone)', async () => {
150
- const child = fork(workerEntryPath(), [], { execArgv: [], silent: true });
158
+ const entry = workerEntryPath();
159
+ const child = fork(entry, [], { execArgv: workerExecArgv(entry), silent: true });
151
160
  doomedPids.push(child.pid);
152
161
  await once(child, 'spawn');
153
162
  const exited = once(child, 'exit');
@@ -171,16 +180,17 @@ describe('① orphan safety net — the worker kills ITSELF when the IPC channel
171
180
  // stdout, then SIGKILLs ITSELF. SIGKILL is the harshest parent death —
172
181
  // no exit handlers, no kill(child), nothing but the OS closing the
173
182
  // IPC pipe. The orphan must notice and exit.
183
+ const entry = workerEntryPath();
174
184
  const helperSource = `
175
185
  const { fork } = require('node:child_process')
176
186
  const fs = require('node:fs')
177
- const child = fork(process.argv[1], [], { execArgv: [], silent: true })
187
+ const child = fork(process.argv[1], [], { execArgv: ${JSON.stringify(workerExecArgv(entry))}, silent: true })
178
188
  child.on('spawn', () => {
179
189
  fs.writeSync(1, 'CHILD_PID=' + child.pid + '\\n')
180
190
  process.kill(process.pid, 'SIGKILL')
181
191
  })
182
192
  `;
183
- const parent = spawn(process.execPath, ['-e', helperSource, workerEntryPath()], {
193
+ const parent = spawn(process.execPath, ['-e', helperSource, entry], {
184
194
  stdio: ['ignore', 'pipe', 'pipe'],
185
195
  });
186
196
  doomedPids.push(parent.pid);
@@ -1 +1 @@
1
- {"version":3,"file":"compile-worker.d.ts","sourceRoot":"","sources":["../src/compile-worker.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAuB,MAAM,2BAA2B,CAAA;AAIvG,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,oBAAoB;IACpC,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,YAAY;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,kBAAkB,CAAA;CAC3B;AAED,MAAM,WAAW,aAAa;IAC7B;;;;;OAKG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IAC3D;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAeD,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,oBAAyB,GAAG,aAAa,CA8KlF"}
1
+ {"version":3,"file":"compile-worker.d.ts","sourceRoot":"","sources":["../src/compile-worker.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAuB,MAAM,2BAA2B,CAAA;AAIvG,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,oBAAoB;IACpC,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,YAAY;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,kBAAkB,CAAA;CAC3B;AAED,MAAM,WAAW,aAAa;IAC7B;;;;;OAKG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IAC3D;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAsCD,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,oBAAyB,GAAG,aAAa,CA6KlF"}
@@ -26,9 +26,10 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
26
  /**
27
27
  * Resolve the fork target. In the published build this is the compiled
28
28
  * `dist/compile-worker-entry.js` sibling; under vitest/dev the module runs
29
- * from `src/`, where only the `.ts` source exists — Node ≥22.18 strips types
30
- * natively, so forking the `.ts` entry works (the entry is a zero-dependency,
31
- * erasable-syntax-only shell by design).
29
+ * from `src/`, where only the `.ts` source exists — the entry is a
30
+ * zero-dependency, erasable-syntax-only shell by design, so Node can run it
31
+ * with type stripping (default since 22.18; behind `--experimental-strip-types`
32
+ * on 22.6–22.17, which `workerExecArgv` passes for a `.ts` entry).
32
33
  */
33
34
  function resolveWorkerEntry() {
34
35
  const js = path.join(__dirname, 'compile-worker-entry.js');
@@ -36,6 +37,26 @@ function resolveWorkerEntry() {
36
37
  return js;
37
38
  return path.join(__dirname, 'compile-worker-entry.ts');
38
39
  }
40
+ /**
41
+ * execArgv for the fork. A compiled `.js` entry (the published build, engines
42
+ * `node >=20`) needs nothing. A `.ts` entry (vitest/dev only) needs Node type
43
+ * stripping: `--experimental-strip-types` exists since 22.6 — on older Node
44
+ * the child would die at startup with an opaque ERR_UNKNOWN_FILE_EXTENSION /
45
+ * bad-option crash, so fail HERE with an actionable message instead. The
46
+ * ExperimentalWarning is silenced because the child's stderr is the dmcc log
47
+ * transport. On ≥22.18 (stripping on by default) the flag is still accepted.
48
+ */
49
+ function workerExecArgv(entry) {
50
+ if (!entry.endsWith('.ts'))
51
+ return [];
52
+ const [major = 0, minor = 0] = process.versions.node.split('.').map(Number);
53
+ const hasStripTypesFlag = major > 22 || (major === 22 && minor >= 6);
54
+ if (!hasStripTypesFlag) {
55
+ throw new Error(`running the compile worker from .ts source requires Node >= 22.6 for type stripping `
56
+ + `(current: ${process.versions.node}); build the package and use the compiled dist entry instead`);
57
+ }
58
+ return ['--experimental-strip-types', '--disable-warning=ExperimentalWarning'];
59
+ }
39
60
  export function createCompileWorker(opts = {}) {
40
61
  let child = null;
41
62
  let closed = false;
@@ -96,10 +117,9 @@ export function createCompileWorker(opts = {}) {
96
117
  const worker = fork(entry, [], {
97
118
  // Pipe stdout/stderr back to the parent — the dmcc log transport.
98
119
  silent: true,
99
- // Do NOT inherit the parent's execArgv (vitest/electron loaders would
100
- // leak into a plain Node child). Node ≥22.18 type-strips a .ts entry
101
- // by default, so no flags are needed either way.
102
- execArgv: [],
120
+ // Explicit execArgv, NOT the parent's (vitest/electron loaders would
121
+ // leak into a plain Node child).
122
+ execArgv: workerExecArgv(entry),
103
123
  });
104
124
  attachLineReader(worker.stdout, 'stdout');
105
125
  attachLineReader(worker.stderr, 'stderr');
@@ -1,8 +1,13 @@
1
1
  import axios from 'axios'
2
2
  import cors from 'cors'
3
3
  import express from 'express'
4
- import { app } from '../server.js'
5
4
 
5
+ /**
6
+ * Register the proxy middleware (cors, json, /proxy) on the CALLER's express
7
+ * app — registration on a shared module-level app would bind these routes to
8
+ * whichever server imported this module first in the process.
9
+ */
10
+ export function register(app) {
6
11
  // 使用cors中间件来允许所有跨域请求
7
12
  // 注意:在实际生产环境中,你应该更具体地配置CORS策略
8
13
  app.use(cors())
@@ -65,4 +70,5 @@ app.post('/proxy', async (req, res) => {
65
70
  })
66
71
  }
67
72
  })
73
+ }
68
74
 
package/fe/index.js CHANGED
@@ -3,7 +3,6 @@ import express from 'express'
3
3
  import path from 'path'
4
4
  import fs from 'fs'
5
5
  import { fileURLToPath } from 'url'
6
- import { app } from './server.js'
7
6
 
8
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
8
 
@@ -18,7 +17,12 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
18
17
  * @returns {Promise<import('http').Server | { server: import('http').Server, reload: () => void }>}
19
18
  */
20
19
  export async function start({ port = 7788, containerDir, outputDir, simulatorDir, liveReload = false, sessionApps = [] } = {}) {
21
- let liveReloadModule
20
+ // One express app PER start(): routes registered on a shared module-level app
21
+ // accumulate across sessions, and express matches the FIRST registration — a
22
+ // second openProject in the same process would get the first session's
23
+ // appList/static/SPA handlers (its dirs possibly gone) and show "暂无应用".
24
+ const app = express()
25
+ let liveReloadHandle
22
26
  const appsDir = outputDir || containerDir
23
27
 
24
28
  app.get('/appList.json', (_req, res) => {
@@ -58,7 +62,8 @@ export async function start({ port = 7788, containerDir, outputDir, simulatorDir
58
62
  }
59
63
 
60
64
  if (liveReload) {
61
- liveReloadModule = await import('./live-reload.js')
65
+ const { createLiveReload } = await import('./live-reload.js')
66
+ liveReloadHandle = createLiveReload(app)
62
67
  // Disable auto index so SPA fallback handles script injection
63
68
  app.use(express.static(containerDir, { index: false }))
64
69
  }
@@ -76,8 +81,9 @@ export async function start({ port = 7788, containerDir, outputDir, simulatorDir
76
81
  })
77
82
  }
78
83
 
79
- // Load proxy server middleware (cors, json, /proxy route)
80
- await import('./dimina-fe-server/index.js')
84
+ // Register proxy server middleware (cors, json, /proxy route)
85
+ const { register: registerProxy } = await import('./dimina-fe-server/index.js')
86
+ registerProxy(app)
81
87
 
82
88
  // SPA fallback: serve index.html for navigation-like unmatched routes
83
89
  // (no extension or `.html`). Asset-like misses (`.json`, `.js`, `.wxml`,
@@ -90,7 +96,7 @@ export async function start({ port = 7788, containerDir, outputDir, simulatorDir
90
96
  // handled promise rejection.
91
97
  // Express 5 requires a named wildcard parameter instead of bare '*'.
92
98
  const spaFallback = liveReload
93
- ? liveReloadModule.injectScript(containerDir)
99
+ ? liveReloadHandle.injectScript(containerDir)
94
100
  : (_req, res) => res.sendFile('index.html', { root: containerDir })
95
101
 
96
102
  app.get('/{*path}', (req, res, next) => {
@@ -107,7 +113,7 @@ export async function start({ port = 7788, containerDir, outputDir, simulatorDir
107
113
  console.log(`Server is running on port ${port}`)
108
114
  console.log('Press Ctrl+C to stop')
109
115
  if (!process.env.DIMINA_NO_OPEN_BROWSER) await open(`http://localhost:${port}`)
110
- resolve(liveReload ? { server, reload: liveReloadModule.reload } : server)
116
+ resolve(liveReload ? { server, reload: liveReloadHandle.reload } : server)
111
117
  })
112
118
  server.on('error', (err) => {
113
119
  if (err.code === 'EADDRINUSE') {
package/fe/live-reload.js CHANGED
@@ -1,30 +1,39 @@
1
1
  import fs from 'node:fs'
2
2
  import path from 'node:path'
3
- import { app } from './server.js'
4
3
 
5
4
  const LIVERELOAD_SCRIPT = `<script>(function(){var es=new EventSource('/__livereload');es.addEventListener('reload',function(){window.location.reload();});}());</script>`
6
5
 
7
- const clients = new Set()
6
+ /**
7
+ * Attach the live-reload SSE endpoint to ONE server's express app and return
8
+ * that server's `reload`/`injectScript`. A module-level client set would
9
+ * broadcast one session's rebuilds into every other session's pages when
10
+ * several servers run in the same process.
11
+ */
12
+ export function createLiveReload(app) {
13
+ const clients = new Set()
8
14
 
9
- app.get('/__livereload', (req, res) => {
10
- res.setHeader('Content-Type', 'text/event-stream')
11
- res.setHeader('Cache-Control', 'no-cache')
12
- res.setHeader('Connection', 'keep-alive')
13
- res.flushHeaders()
14
- clients.add(res)
15
- req.on('close', () => clients.delete(res))
16
- })
15
+ app.get('/__livereload', (req, res) => {
16
+ res.setHeader('Content-Type', 'text/event-stream')
17
+ res.setHeader('Cache-Control', 'no-cache')
18
+ res.setHeader('Connection', 'keep-alive')
19
+ res.flushHeaders()
20
+ clients.add(res)
21
+ req.on('close', () => clients.delete(res))
22
+ })
17
23
 
18
- export function reload() {
19
- for (const client of clients) {
20
- client.write('event: reload\ndata: \n\n')
24
+ function reload() {
25
+ for (const client of clients) {
26
+ client.write('event: reload\ndata: \n\n')
27
+ }
21
28
  }
22
- }
23
29
 
24
- export function injectScript(containerDir) {
25
- return (_req, res) => {
26
- const html = fs.readFileSync(path.join(containerDir, 'index.html'), 'utf-8')
27
- res.setHeader('Content-Type', 'text/html')
28
- res.send(html.replace('</body>', `${LIVERELOAD_SCRIPT}</body>`))
30
+ function injectScript(containerDir) {
31
+ return (_req, res) => {
32
+ const html = fs.readFileSync(path.join(containerDir, 'index.html'), 'utf-8')
33
+ res.setHeader('Content-Type', 'text/html')
34
+ res.send(html.replace('</body>', `${LIVERELOAD_SCRIPT}</body>`))
35
+ }
29
36
  }
37
+
38
+ return { reload, injectScript }
30
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dimina-kit/devkit",
3
- "version": "0.1.2-dev.20260701155140",
3
+ "version": "0.1.2-dev.20260702145840",
4
4
  "description": "Development toolkit for Dimina mini-apps with H5 container preview",
5
5
  "keywords": [
6
6
  "dimina",
@@ -57,12 +57,12 @@
57
57
  "@dimina-kit/eslint-config": "0.1.0"
58
58
  },
59
59
  "dependencies": {
60
- "@dimina/compiler": "1.0.17",
61
60
  "axios": "^1.15.0",
62
61
  "chokidar": "^5.0.0",
63
62
  "cors": "^2.8.6",
64
63
  "express": "^5.2.1",
65
- "open": "^11.0.0"
64
+ "open": "^11.0.0",
65
+ "@dimina-kit/compiler": "0.0.1"
66
66
  },
67
67
  "publishConfig": {
68
68
  "access": "public"