ms-vite-plugin 1.0.3 → 1.0.5

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/dist/cli.js CHANGED
@@ -40,12 +40,18 @@ const path = __importStar(require("path"));
40
40
  const build_1 = require("./build");
41
41
  const fsExtra = __importStar(require("fs-extra"));
42
42
  const project_1 = require("./project");
43
+ const packager_1 = require("./packager");
43
44
  const ws_manager_1 = require("./ws-manager");
45
+ /**
46
+ * WS 状态持久化文件路径(用于跨进程查询 ws-status)
47
+ */
44
48
  const WS_PID_FILE = path.join(os.tmpdir(), "ms-cli-ws-server.json");
45
49
  /**
46
50
  * 检查工作目录是否为有效的 KuaiJS 项目
47
51
  * @param workspacePath 工作目录路径
48
52
  * @returns 是否为有效项目
53
+ * @example
54
+ * await isValidKuaiJSProject(process.cwd())
49
55
  */
50
56
  async function isValidKuaiJSProject(workspacePath) {
51
57
  try {
@@ -61,24 +67,36 @@ async function isValidKuaiJSProject(workspacePath) {
61
67
  return false;
62
68
  }
63
69
  }
70
+ /**
71
+ * 统一校验项目目录合法性
72
+ * @param workspacePath 待校验的项目目录
73
+ * @returns 校验通过返回 Promise<void>
74
+ * @example
75
+ * await ensureValidKuaiJSProject("/Users/demo/project")
76
+ */
77
+ async function ensureValidKuaiJSProject(workspacePath) {
78
+ if (await isValidKuaiJSProject(workspacePath)) {
79
+ return;
80
+ }
81
+ console.error("❌ 错误: 当前目录不是有效的 快点JS 项目");
82
+ console.error("请确保目录包含以下文件:");
83
+ console.error(" - package.json");
84
+ console.error(" - scripts/ 目录");
85
+ process.exit(1);
86
+ }
64
87
  /**
65
88
  * 构建命令处理函数
66
89
  * @param options 命令选项
90
+ * @returns 请求完成后退出
91
+ * @example
92
+ * ms build -d
67
93
  */
68
94
  async function buildCommand(options) {
69
95
  try {
70
- const workspacePath = options.path
71
- ? path.resolve(options.path)
72
- : process.cwd();
96
+ const workspacePath = process.cwd();
73
97
  const isDev = options.dev || false;
74
98
  console.log(`🔍 检查项目目录: ${workspacePath}`);
75
- if (!(await isValidKuaiJSProject(workspacePath))) {
76
- console.error("❌ 错误: 当前目录不是有效的 快点JS 项目");
77
- console.error("请确保目录包含以下文件:");
78
- console.error(" - package.json");
79
- console.error(" - scripts/ 目录");
80
- process.exit(1);
81
- }
99
+ await ensureValidKuaiJSProject(workspacePath);
82
100
  console.log(`🚀 开始构建项目 (${isDev ? "开发模式" : "生产模式"})...`);
83
101
  await (0, build_1.buildAll)(isDev, workspacePath);
84
102
  console.log("✅ 构建完成!");
@@ -91,17 +109,38 @@ async function buildCommand(options) {
91
109
  process.exit(1);
92
110
  }
93
111
  }
112
+ /**
113
+ * package 命令处理函数(生产构建 + 加密)
114
+ * @returns 请求完成后退出
115
+ * @example
116
+ * ms package
117
+ */
118
+ async function packageCommand() {
119
+ try {
120
+ const workspacePath = process.cwd();
121
+ console.log(`🔍 检查项目目录: ${workspacePath}`);
122
+ await ensureValidKuaiJSProject(workspacePath);
123
+ console.log("📦 开始打包生产构建并加密 bundle...");
124
+ const encryptedPath = await (0, packager_1.packageProject)(workspacePath);
125
+ console.log(`✅ 打包完成: ${encryptedPath}`);
126
+ }
127
+ catch (error) {
128
+ console.error("❌ 打包失败:", error instanceof Error ? error.message : error);
129
+ process.exit(1);
130
+ }
131
+ }
94
132
  /**
95
133
  * run 命令处理函数
96
134
  * @param options 命令选项
97
135
  * @returns 请求完成后退出
98
136
  * @example
99
- * ms run --ip 192.168.1.100 --port 9800 --path ./
137
+ * ms run --ip 192.168.1.100 --port 9800
100
138
  */
101
139
  async function runCommand(options) {
102
140
  try {
141
+ const workspacePath = process.cwd();
142
+ await ensureValidKuaiJSProject(workspacePath);
103
143
  await (0, project_1.runOnDevice)(options);
104
- console.log("✅ 运行请求已发送");
105
144
  }
106
145
  catch (error) {
107
146
  console.error("❌ 运行失败:", error instanceof Error ? error.message : error);
@@ -113,12 +152,13 @@ async function runCommand(options) {
113
152
  * @param options 命令选项
114
153
  * @returns 请求完成后退出
115
154
  * @example
116
- * ms run-ui --ip 192.168.1.100 --port 9800 --path ./
155
+ * ms run-ui --ip 192.168.1.100 --port 9800
117
156
  */
118
157
  async function runUICommand(options) {
119
158
  try {
159
+ const workspacePath = process.cwd();
160
+ await ensureValidKuaiJSProject(workspacePath);
120
161
  await (0, project_1.runUIOnDevice)(options);
121
- console.log("✅ UI 预览请求已发送");
122
162
  }
123
163
  catch (error) {
124
164
  console.error("❌ UI 预览失败:", error instanceof Error ? error.message : error);
@@ -134,8 +174,9 @@ async function runUICommand(options) {
134
174
  */
135
175
  async function stopCommand(options) {
136
176
  try {
177
+ const workspacePath = process.cwd();
178
+ await ensureValidKuaiJSProject(workspacePath);
137
179
  await (0, project_1.stopOnDevice)(options);
138
- console.log("✅ 停止请求已发送");
139
180
  }
140
181
  catch (error) {
141
182
  console.error("❌ 停止失败:", error instanceof Error ? error.message : error);
@@ -151,6 +192,7 @@ async function stopCommand(options) {
151
192
  */
152
193
  async function wsStartCommand(options) {
153
194
  try {
195
+ await ensureValidKuaiJSProject(process.cwd());
154
196
  const wsPortText = (options.wsPort ?? "31111").trim();
155
197
  const wsPort = Number.parseInt(wsPortText, 10);
156
198
  if (!Number.isInteger(wsPort) || wsPort < 1 || wsPort > 65535) {
@@ -158,17 +200,40 @@ async function wsStartCommand(options) {
158
200
  }
159
201
  const ws = ws_manager_1.WSManager.get();
160
202
  await ws.start(wsPort);
203
+ /**
204
+ * 写入 WS 运行状态到临时文件,供 ws-status 命令跨进程读取
205
+ */
206
+ const writeStatus = async () => {
207
+ const payload = {
208
+ pid: process.pid,
209
+ port: wsPort,
210
+ address: ws.getAddress(),
211
+ startedAt: Date.now(),
212
+ connected: ws.isClientConnected(),
213
+ clientIp: ws.getConnectedClientIp(),
214
+ };
215
+ await fsExtra.writeJSON(WS_PID_FILE, payload);
216
+ };
217
+ await writeStatus();
218
+ const statusTimer = setInterval(() => {
219
+ writeStatus().catch(() => {
220
+ // ignore periodic write errors
221
+ });
222
+ }, 1000);
161
223
  await fsExtra.writeJSON(WS_PID_FILE, {
162
224
  pid: process.pid,
163
225
  port: wsPort,
164
226
  address: ws.getAddress(),
165
227
  startedAt: Date.now(),
228
+ connected: ws.isClientConnected(),
229
+ clientIp: ws.getConnectedClientIp(),
166
230
  });
167
231
  console.log("按 Ctrl+C 停止 WS 服务");
168
232
  await new Promise((resolve) => {
169
233
  const shutdown = async () => {
170
234
  process.off("SIGINT", shutdown);
171
235
  process.off("SIGTERM", shutdown);
236
+ clearInterval(statusTimer);
172
237
  await ws.stop();
173
238
  await fsExtra.remove(WS_PID_FILE);
174
239
  resolve();
@@ -190,6 +255,7 @@ async function wsStartCommand(options) {
190
255
  */
191
256
  async function wsStopCommand() {
192
257
  try {
258
+ await ensureValidKuaiJSProject(process.cwd());
193
259
  if (!(await fsExtra.pathExists(WS_PID_FILE))) {
194
260
  console.log("WS 服务未运行");
195
261
  return;
@@ -221,6 +287,7 @@ async function wsStopCommand() {
221
287
  * ms ws-status
222
288
  */
223
289
  async function wsStatusCommand() {
290
+ await ensureValidKuaiJSProject(process.cwd());
224
291
  if (!(await fsExtra.pathExists(WS_PID_FILE))) {
225
292
  console.log("服务状态: stopped");
226
293
  return;
@@ -228,6 +295,10 @@ async function wsStatusCommand() {
228
295
  const info = await fsExtra.readJSON(WS_PID_FILE);
229
296
  const pid = Number(info.pid);
230
297
  const address = String(info.address || "unknown");
298
+ const connected = Boolean(info.connected);
299
+ const clientIp = info.clientIp === null || info.clientIp === undefined
300
+ ? null
301
+ : String(info.clientIp);
231
302
  let running = false;
232
303
  try {
233
304
  process.kill(pid, 0);
@@ -243,16 +314,50 @@ async function wsStatusCommand() {
243
314
  }
244
315
  console.log(`服务状态: running (pid=${pid})`);
245
316
  console.log(`WS 地址: ${address}`);
317
+ console.log(`连接状态: ${connected ? "connected" : "disconnected"}`);
318
+ if (connected) {
319
+ console.log(`设备IP: ${clientIp || "unknown"}`);
320
+ }
321
+ }
322
+ /**
323
+ * 生成顶层 help 的详细命令参数说明
324
+ * @returns 返回格式化后的帮助文本
325
+ * @example
326
+ * renderDetailedCommandHelp()
327
+ */
328
+ function renderDetailedCommandHelp() {
329
+ const lines = [];
330
+ lines.push("");
331
+ lines.push("详细命令参数:");
332
+ const commands = commander_1.program.commands.filter((cmd) => cmd.name() !== "help");
333
+ for (const cmd of commands) {
334
+ lines.push(` ${cmd.name()}:`);
335
+ if (cmd.options.length === 0) {
336
+ lines.push(" (无额外参数)");
337
+ continue;
338
+ }
339
+ for (const opt of cmd.options) {
340
+ const desc = opt.description ? ` ${opt.description}` : "";
341
+ lines.push(` ${opt.flags}${desc}`);
342
+ }
343
+ }
344
+ lines.push("");
345
+ return lines.join("\n");
246
346
  }
247
347
  // 配置 CLI 程序
248
- commander_1.program.name("ms-cli").description("快点JS 构建工具").version("0.0.7");
348
+ commander_1.program.name("ms-cli").description("快点JS 构建工具").version("1.0.4");
349
+ commander_1.program.addHelpText("after", renderDetailedCommandHelp);
249
350
  // 构建命令
250
351
  commander_1.program
251
352
  .command("build")
252
353
  .description("构建 快点JS 项目")
253
354
  .option("-d, --dev", "开发模式构建 (包含 source map)")
254
- .option("-p, --path <path>", "指定项目路径")
255
355
  .action(buildCommand);
356
+ // 打包命令
357
+ commander_1.program
358
+ .command("package")
359
+ .description("生产构建并加密 ms.msbundle")
360
+ .action(packageCommand);
256
361
  // 运行命令
257
362
  commander_1.program
258
363
  .command("run")
@@ -262,7 +367,6 @@ commander_1.program
262
367
  .option("-t, --transport <transport>", "传输方式: http|ws", "http")
263
368
  .option("--ws-port <wsPort>", "WS 服务端口 (默认: 31111)", "31111")
264
369
  .option("--ws-wait-ms <wsWaitMs>", "WS 等待设备连接时间(毫秒)", "30000")
265
- .option("-p, --path <path>", "指定项目路径")
266
370
  .action(runCommand);
267
371
  // UI 预览命令
268
372
  commander_1.program
@@ -273,7 +377,6 @@ commander_1.program
273
377
  .option("-t, --transport <transport>", "传输方式: http|ws", "http")
274
378
  .option("--ws-port <wsPort>", "WS 服务端口 (默认: 31111)", "31111")
275
379
  .option("--ws-wait-ms <wsWaitMs>", "WS 等待设备连接时间(毫秒)", "30000")
276
- .option("-p, --path <path>", "指定项目路径")
277
380
  .action(runUICommand);
278
381
  // 停止命令
279
382
  commander_1.program
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 执行 package 流程(生产构建 + bundle 加密)
3
+ * @param workspacePath 当前工作目录(项目根目录)
4
+ * @returns 返回加密产物 enc.msbundle 的绝对路径
5
+ * @example
6
+ * const output = await packageProject(process.cwd())
7
+ */
8
+ export declare function packageProject(workspacePath: string): Promise<string>;
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.packageProject = packageProject;
37
+ const child_process_1 = require("child_process");
38
+ const fsExtra = __importStar(require("fs-extra"));
39
+ const path = __importStar(require("path"));
40
+ const util_1 = require("util");
41
+ const build_1 = require("./build");
42
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
43
+ /**
44
+ * 打包相关常量
45
+ */
46
+ const PACKAGE_CONSTANTS = {
47
+ /** 加密超时时间(毫秒) */
48
+ ENCRYPT_TIMEOUT: 30000,
49
+ /** 明文 bundle 文件名 */
50
+ BUNDLE_NAME: "ms.msbundle",
51
+ /** 加密后 bundle 文件名 */
52
+ ENCRYPTED_BUNDLE_NAME: "enc.msbundle",
53
+ };
54
+ /**
55
+ * 获取当前插件根目录
56
+ * @returns 插件根目录绝对路径
57
+ * @example
58
+ * const root = getPluginRoot()
59
+ */
60
+ function getPluginRoot() {
61
+ return path.resolve(__dirname, "..");
62
+ }
63
+ /**
64
+ * 获取对应系统的加密工具路径
65
+ * @returns 加密工具绝对路径
66
+ * @example
67
+ * const encryptorPath = getEncryptorPath()
68
+ */
69
+ function getEncryptorPath() {
70
+ const platform = process.platform;
71
+ const arch = process.arch;
72
+ let binSubPath;
73
+ if (platform === "darwin") {
74
+ binSubPath = path.join("bin", "mac", "ms-bundle-enc");
75
+ }
76
+ else if (platform === "win32" && arch === "x64") {
77
+ binSubPath = path.join("bin", "win", "ms-bundle-enc.exe");
78
+ }
79
+ else {
80
+ throw new Error(`不支持的系统平台: ${platform}-${arch}`);
81
+ }
82
+ return path.join(getPluginRoot(), binSubPath);
83
+ }
84
+ /**
85
+ * 加密构建产物 bundle 文件
86
+ * @param bundlePath 明文 bundle 绝对路径
87
+ * @returns 返回加密后的 bundle 绝对路径
88
+ * @example
89
+ * const encryptedPath = await encryptBundle("/tmp/dist/ms.msbundle")
90
+ */
91
+ async function encryptBundle(bundlePath) {
92
+ const encryptorPath = getEncryptorPath();
93
+ if (!(await fsExtra.pathExists(encryptorPath))) {
94
+ throw new Error(`未找到加密工具: ${encryptorPath}`);
95
+ }
96
+ const isWindows = process.platform === "win32";
97
+ if (!isWindows) {
98
+ await fsExtra.chmod(encryptorPath, 0o755);
99
+ }
100
+ await execFileAsync(encryptorPath, [bundlePath], {
101
+ cwd: path.dirname(bundlePath),
102
+ timeout: PACKAGE_CONSTANTS.ENCRYPT_TIMEOUT,
103
+ });
104
+ const encryptedPath = path.join(path.dirname(bundlePath), PACKAGE_CONSTANTS.ENCRYPTED_BUNDLE_NAME);
105
+ if (!(await fsExtra.pathExists(encryptedPath))) {
106
+ throw new Error("加密工具未生成 enc.msbundle 文件");
107
+ }
108
+ await fsExtra.remove(bundlePath);
109
+ return encryptedPath;
110
+ }
111
+ /**
112
+ * 执行 package 流程(生产构建 + bundle 加密)
113
+ * @param workspacePath 当前工作目录(项目根目录)
114
+ * @returns 返回加密产物 enc.msbundle 的绝对路径
115
+ * @example
116
+ * const output = await packageProject(process.cwd())
117
+ */
118
+ async function packageProject(workspacePath) {
119
+ await (0, build_1.buildAll)(false, workspacePath);
120
+ const bundlePath = path.join(workspacePath, "dist", PACKAGE_CONSTANTS.BUNDLE_NAME);
121
+ if (!(await fsExtra.pathExists(bundlePath))) {
122
+ throw new Error(`构建失败,未找到 ${PACKAGE_CONSTANTS.BUNDLE_NAME} 文件`);
123
+ }
124
+ return encryptBundle(bundlePath);
125
+ }
package/dist/project.d.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  export interface DeviceCliOptions {
5
5
  ip?: string;
6
6
  port?: string;
7
- path?: string;
8
7
  transport?: string;
9
8
  wsPort?: string;
10
9
  wsWaitMs?: string;
package/dist/project.js CHANGED
@@ -403,9 +403,7 @@ async function ensureWsReady(options) {
403
403
  * await runOnDevice({ ip: "192.168.1.10", port: "9800", path: "./demo" })
404
404
  */
405
405
  async function runOnDevice(options) {
406
- const workspacePath = options.path
407
- ? path.resolve(options.path)
408
- : process.cwd();
406
+ const workspacePath = process.cwd();
409
407
  const transport = parseTransport(options);
410
408
  console.log(`🔧 开始构建项目: ${workspacePath}`);
411
409
  await runDevBuild(workspacePath);
@@ -433,9 +431,7 @@ async function runOnDevice(options) {
433
431
  * await runUIOnDevice({ ip: "192.168.1.10", port: "9800", path: "./demo" })
434
432
  */
435
433
  async function runUIOnDevice(options) {
436
- const workspacePath = options.path
437
- ? path.resolve(options.path)
438
- : process.cwd();
434
+ const workspacePath = process.cwd();
439
435
  const transport = parseTransport(options);
440
436
  console.log(`🔧 开始构建项目: ${workspacePath}`);
441
437
  await runDevBuild(workspacePath);
@@ -15,6 +15,7 @@ export declare class WSManager {
15
15
  private wss;
16
16
  private client;
17
17
  private checkInterval;
18
+ private connectedClientIp;
18
19
  private readonly hostIp;
19
20
  private port;
20
21
  private pending;
@@ -56,6 +57,20 @@ export declare class WSManager {
56
57
  * ws.isStarted()
57
58
  */
58
59
  isStarted(): boolean;
60
+ /**
61
+ * 获取设备连接状态
62
+ * @returns 已连接返回 true,否则 false
63
+ * @example
64
+ * ws.isClientConnected()
65
+ */
66
+ isClientConnected(): boolean;
67
+ /**
68
+ * 获取当前连接设备 IP
69
+ * @returns 返回设备 IP,未连接时返回 null
70
+ * @example
71
+ * ws.getConnectedClientIp()
72
+ */
73
+ getConnectedClientIp(): string | null;
59
74
  /**
60
75
  * 启动 WS 服务
61
76
  * @param port 指定端口,默认 31111
@@ -105,6 +105,12 @@ class WSManager {
105
105
  writable: true,
106
106
  value: null
107
107
  });
108
+ Object.defineProperty(this, "connectedClientIp", {
109
+ enumerable: true,
110
+ configurable: true,
111
+ writable: true,
112
+ value: null
113
+ });
108
114
  Object.defineProperty(this, "hostIp", {
109
115
  enumerable: true,
110
116
  configurable: true,
@@ -175,6 +181,24 @@ class WSManager {
175
181
  isStarted() {
176
182
  return this.wss !== null;
177
183
  }
184
+ /**
185
+ * 获取设备连接状态
186
+ * @returns 已连接返回 true,否则 false
187
+ * @example
188
+ * ws.isClientConnected()
189
+ */
190
+ isClientConnected() {
191
+ return this.client !== null;
192
+ }
193
+ /**
194
+ * 获取当前连接设备 IP
195
+ * @returns 返回设备 IP,未连接时返回 null
196
+ * @example
197
+ * ws.getConnectedClientIp()
198
+ */
199
+ getConnectedClientIp() {
200
+ return this.connectedClientIp;
201
+ }
178
202
  /**
179
203
  * 启动 WS 服务
180
204
  * @param port 指定端口,默认 31111
@@ -196,12 +220,14 @@ class WSManager {
196
220
  return;
197
221
  }
198
222
  this.client = ws;
223
+ const socket = ws;
224
+ this.connectedClientIp = socket._socket?.remoteAddress || null;
199
225
  ws.on("close", () => {
200
226
  if (this.client === ws) {
201
- const socket = ws;
202
- console.log(`设备已断开,设备IP: ${socket._socket?.remoteAddress || "unknown"}`);
227
+ console.log(`设备已断开,设备IP: ${this.connectedClientIp || "unknown"}`);
203
228
  this.stopStatusInterval();
204
229
  this.client = null;
230
+ this.connectedClientIp = null;
205
231
  }
206
232
  });
207
233
  ws.on("message", (data) => {
@@ -224,8 +250,7 @@ class WSManager {
224
250
  p.resolve(message);
225
251
  }
226
252
  });
227
- const socket = ws;
228
- console.log(`设备已连接,设备IP: ${socket._socket?.remoteAddress || "unknown"}`);
253
+ console.log(`设备已连接,设备IP: ${this.connectedClientIp || "unknown"}`);
229
254
  this.startStatusInterval();
230
255
  });
231
256
  await new Promise((resolve, reject) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ms-vite-plugin",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "type": "commonjs",
5
5
  "license": "MIT",
6
6
  "publishConfig": {