@hapico/cli 0.0.14 → 0.0.16

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/bin/index.js CHANGED
@@ -38,6 +38,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.compileES5 = void 0;
41
+ /* eslint-disable @typescript-eslint/no-explicit-any */
42
+ const lodash_1 = require("lodash");
41
43
  const commander_1 = require("commander");
42
44
  const axios_1 = __importDefault(require("axios"));
43
45
  const fs = __importStar(require("fs"));
@@ -226,7 +228,7 @@ class FileManager {
226
228
  }
227
229
  }
228
230
  class RoomState {
229
- constructor(roomId) {
231
+ constructor(roomId, onChangeListeners) {
230
232
  this.files = [];
231
233
  this.roomId = roomId;
232
234
  this.state = {};
@@ -234,6 +236,7 @@ class RoomState {
234
236
  this.ws = null;
235
237
  this.reconnectTimeout = null;
236
238
  this.reconnectAttempts = 0;
239
+ this.onChangeListeners = onChangeListeners;
237
240
  }
238
241
  connect(onConnected) {
239
242
  if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN)
@@ -242,25 +245,58 @@ class RoomState {
242
245
  clearTimeout(this.reconnectTimeout);
243
246
  }
244
247
  this.ws = new ws_1.WebSocket(`https://base.myworkbeast.com/ws?room=${this.roomId}`);
245
- this.ws.on("open", () => {
248
+ this.ws.onopen = () => {
249
+ console.log(`Connected to room: ${this.roomId}`);
246
250
  this.isConnected = true;
247
251
  this.reconnectAttempts = 0;
248
- onConnected === null || onConnected === void 0 ? void 0 : onConnected();
249
- connected.succeed(`Connected to WebSocket server`);
250
- });
251
- this.ws.on("close", () => {
252
+ onConnected === null || onConnected === void 0 ? void 0 : onConnected(); // Call the onConnected callback if provided
253
+ };
254
+ this.ws.onclose = () => {
255
+ console.log(`Disconnected from room: ${this.roomId}`);
252
256
  this.isConnected = false;
253
257
  this.reconnectAttempts++;
254
- connected.start(`Retry connection...`);
255
- this.connect();
256
- });
258
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
259
+ console.log(`Attempting to reconnect in ${delay / 1000}s...`);
260
+ this.reconnectTimeout = setTimeout(() => this.connect(onConnected), delay);
261
+ };
257
262
  this.ws.on("message", (data) => {
258
263
  try {
259
- const message = JSON.parse(data.toString());
260
- if (message.type === "state" && message.state) {
261
- this.state = message.state;
262
- const view = this.state.view;
263
- this.files = view;
264
+ const jsonStr = typeof data === "string" ? data : data.toString();
265
+ const parsedData = JSON.parse(jsonStr);
266
+ const includes = [
267
+ "view",
268
+ "coding",
269
+ "refresh_key",
270
+ "useActiveIdFocus",
271
+ "activeId",
272
+ "active_file",
273
+ "figma",
274
+ ];
275
+ let newState;
276
+ let changedKeys;
277
+ if (parsedData.type === "state") {
278
+ // Full state (e.g., on initial connect)
279
+ newState = parsedData.state;
280
+ changedKeys = Object.keys(newState).filter((key) => !(0, lodash_1.isEqual)(newState[key], this.state[key]));
281
+ }
282
+ else if (parsedData.type === "update") {
283
+ // Delta update
284
+ const delta = parsedData.state;
285
+ newState = { ...this.state, ...delta };
286
+ changedKeys = Object.keys(delta).filter((key) => !(0, lodash_1.isEqual)(delta[key], this.state[key]));
287
+ }
288
+ else {
289
+ return;
290
+ }
291
+ const filteredChangedKeys = changedKeys.filter((key) => includes.includes(key));
292
+ if (filteredChangedKeys.length > 0) {
293
+ filteredChangedKeys.forEach((key) => {
294
+ const listener = (0, lodash_1.find)(this.onChangeListeners, (l) => l.key === key);
295
+ if (listener) {
296
+ listener.callback(newState[key]);
297
+ }
298
+ });
299
+ this.state = newState;
264
300
  }
265
301
  }
266
302
  catch (e) {
@@ -280,16 +316,13 @@ class RoomState {
280
316
  state: { [key]: value },
281
317
  }));
282
318
  }
283
- else {
284
- console.log("Cannot update state: Not connected");
285
- }
286
319
  }
287
320
  disconnect() {
288
321
  if (this.reconnectTimeout) {
289
322
  clearTimeout(this.reconnectTimeout);
290
323
  }
291
324
  if (this.ws) {
292
- this.ws.removeAllListeners("close");
325
+ this.ws.onclose = null; // Prevent reconnect on intentional close
293
326
  this.ws.close();
294
327
  }
295
328
  }
@@ -300,7 +333,7 @@ class RoomState {
300
333
  return this.isConnected;
301
334
  }
302
335
  }
303
- commander_1.program.version("0.0.14").description("Hapico CLI for project management");
336
+ commander_1.program.version("0.0.16").description("Hapico CLI for project management");
304
337
  commander_1.program
305
338
  .command("clone <id>")
306
339
  .description("Clone a project by ID")
@@ -411,22 +444,25 @@ commander_1.program
411
444
  return;
412
445
  }
413
446
  console.log(`Connecting to WebSocket server`);
414
- const room = new RoomState(`view_${projectId}`);
447
+ const room = new RoomState(`view_${projectId}`, []);
448
+ const fileManager = new FileManager(srcDir);
449
+ const initialFiles = fileManager.listFiles();
450
+ room.files = initialFiles;
415
451
  room.connect(async () => {
416
452
  devSpinner.succeed("Project started in development mode!");
417
- const fileManager = new FileManager(srcDir);
418
- const initialFiles = fileManager.listFiles();
419
453
  room.updateState("view", initialFiles);
420
454
  fileManager.setOnFileChange((filePath, content) => {
421
- const es5 = (0, exports.compileES5)(content, filePath);
455
+ var _a;
456
+ const es5 = (_a = (0, exports.compileES5)(content, filePath)) !== null && _a !== void 0 ? _a : "";
422
457
  console.log(`File changed: ${filePath === null || filePath === void 0 ? void 0 : filePath.replace(srcDir, ".")}`);
423
- const updatedView = room.files.map((file) => {
458
+ const updatedFiles = room.files.map((file) => {
424
459
  if (path.join(srcDir, file.path) === filePath) {
425
460
  return { ...file, content, es5 };
426
461
  }
427
462
  return file;
428
463
  });
429
- room.updateState("view", updatedView);
464
+ room.files = updatedFiles;
465
+ room.updateState("view", updatedFiles);
430
466
  });
431
467
  // Fetch project info
432
468
  const projectInfo = getStoredProjectId(pwd);
@@ -445,7 +481,6 @@ commander_1.program
445
481
  return;
446
482
  }
447
483
  const projectType = project.data.type || "view";
448
- console.log("Project type:", projectType);
449
484
  if (projectType === "zalominiapp") {
450
485
  qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}`, { small: true }, (qrcode) => {
451
486
  console.log("Scan this QR code to connect to the project:");
package/bun.lock CHANGED
@@ -7,6 +7,7 @@
7
7
  "@babel/standalone": "^7.28.2",
8
8
  "axios": "^1.11.0",
9
9
  "commander": "^14.0.0",
10
+ "lodash": "^4.17.21",
10
11
  "open": "^10.2.0",
11
12
  "ora": "^8.2.0",
12
13
  "qrcode-terminal": "^0.12.0",
@@ -16,6 +17,7 @@
16
17
  "devDependencies": {
17
18
  "@types/babel__standalone": "^7.1.9",
18
19
  "@types/commander": "^2.12.5",
20
+ "@types/lodash": "^4.17.20",
19
21
  "@types/node": "^24.1.0",
20
22
  "@types/qrcode-terminal": "^0.12.2",
21
23
  "@types/unzipper": "^0.10.11",
@@ -47,6 +49,8 @@
47
49
 
48
50
  "@types/commander": ["@types/commander@2.12.5", "", { "dependencies": { "commander": "*" } }, "sha512-YXGZ/rz+s57VbzcvEV9fUoXeJlBt5HaKu5iUheiIWNsJs23bz6AnRuRiZBRVBLYyPnixNvVnuzM5pSaxr8Yp/g=="],
49
51
 
52
+ "@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="],
53
+
50
54
  "@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
51
55
 
52
56
  "@types/qrcode-terminal": ["@types/qrcode-terminal@0.12.2", "", {}, "sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q=="],
@@ -141,6 +145,8 @@
141
145
 
142
146
  "jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
143
147
 
148
+ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
149
+
144
150
  "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="],
145
151
 
146
152
  "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
package/dist/index.js CHANGED
@@ -38,6 +38,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.compileES5 = void 0;
41
+ /* eslint-disable @typescript-eslint/no-explicit-any */
42
+ const lodash_1 = require("lodash");
41
43
  const commander_1 = require("commander");
42
44
  const axios_1 = __importDefault(require("axios"));
43
45
  const fs = __importStar(require("fs"));
@@ -226,7 +228,7 @@ class FileManager {
226
228
  }
227
229
  }
228
230
  class RoomState {
229
- constructor(roomId) {
231
+ constructor(roomId, onChangeListeners) {
230
232
  this.files = [];
231
233
  this.roomId = roomId;
232
234
  this.state = {};
@@ -234,6 +236,7 @@ class RoomState {
234
236
  this.ws = null;
235
237
  this.reconnectTimeout = null;
236
238
  this.reconnectAttempts = 0;
239
+ this.onChangeListeners = onChangeListeners;
237
240
  }
238
241
  connect(onConnected) {
239
242
  if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN)
@@ -242,25 +245,58 @@ class RoomState {
242
245
  clearTimeout(this.reconnectTimeout);
243
246
  }
244
247
  this.ws = new ws_1.WebSocket(`https://base.myworkbeast.com/ws?room=${this.roomId}`);
245
- this.ws.on("open", () => {
248
+ this.ws.onopen = () => {
249
+ console.log(`Connected to room: ${this.roomId}`);
246
250
  this.isConnected = true;
247
251
  this.reconnectAttempts = 0;
248
- onConnected === null || onConnected === void 0 ? void 0 : onConnected();
249
- connected.succeed(`Connected to WebSocket server`);
250
- });
251
- this.ws.on("close", () => {
252
+ onConnected === null || onConnected === void 0 ? void 0 : onConnected(); // Call the onConnected callback if provided
253
+ };
254
+ this.ws.onclose = () => {
255
+ console.log(`Disconnected from room: ${this.roomId}`);
252
256
  this.isConnected = false;
253
257
  this.reconnectAttempts++;
254
- connected.start(`Retry connection...`);
255
- this.connect();
256
- });
258
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
259
+ console.log(`Attempting to reconnect in ${delay / 1000}s...`);
260
+ this.reconnectTimeout = setTimeout(() => this.connect(onConnected), delay);
261
+ };
257
262
  this.ws.on("message", (data) => {
258
263
  try {
259
- const message = JSON.parse(data.toString());
260
- if (message.type === "state" && message.state) {
261
- this.state = message.state;
262
- const view = this.state.view;
263
- this.files = view;
264
+ const jsonStr = typeof data === "string" ? data : data.toString();
265
+ const parsedData = JSON.parse(jsonStr);
266
+ const includes = [
267
+ "view",
268
+ "coding",
269
+ "refresh_key",
270
+ "useActiveIdFocus",
271
+ "activeId",
272
+ "active_file",
273
+ "figma",
274
+ ];
275
+ let newState;
276
+ let changedKeys;
277
+ if (parsedData.type === "state") {
278
+ // Full state (e.g., on initial connect)
279
+ newState = parsedData.state;
280
+ changedKeys = Object.keys(newState).filter((key) => !(0, lodash_1.isEqual)(newState[key], this.state[key]));
281
+ }
282
+ else if (parsedData.type === "update") {
283
+ // Delta update
284
+ const delta = parsedData.state;
285
+ newState = { ...this.state, ...delta };
286
+ changedKeys = Object.keys(delta).filter((key) => !(0, lodash_1.isEqual)(delta[key], this.state[key]));
287
+ }
288
+ else {
289
+ return;
290
+ }
291
+ const filteredChangedKeys = changedKeys.filter((key) => includes.includes(key));
292
+ if (filteredChangedKeys.length > 0) {
293
+ filteredChangedKeys.forEach((key) => {
294
+ const listener = (0, lodash_1.find)(this.onChangeListeners, (l) => l.key === key);
295
+ if (listener) {
296
+ listener.callback(newState[key]);
297
+ }
298
+ });
299
+ this.state = newState;
264
300
  }
265
301
  }
266
302
  catch (e) {
@@ -280,16 +316,13 @@ class RoomState {
280
316
  state: { [key]: value },
281
317
  }));
282
318
  }
283
- else {
284
- console.log("Cannot update state: Not connected");
285
- }
286
319
  }
287
320
  disconnect() {
288
321
  if (this.reconnectTimeout) {
289
322
  clearTimeout(this.reconnectTimeout);
290
323
  }
291
324
  if (this.ws) {
292
- this.ws.removeAllListeners("close");
325
+ this.ws.onclose = null; // Prevent reconnect on intentional close
293
326
  this.ws.close();
294
327
  }
295
328
  }
@@ -300,7 +333,7 @@ class RoomState {
300
333
  return this.isConnected;
301
334
  }
302
335
  }
303
- commander_1.program.version("0.0.14").description("Hapico CLI for project management");
336
+ commander_1.program.version("0.0.16").description("Hapico CLI for project management");
304
337
  commander_1.program
305
338
  .command("clone <id>")
306
339
  .description("Clone a project by ID")
@@ -411,22 +444,25 @@ commander_1.program
411
444
  return;
412
445
  }
413
446
  console.log(`Connecting to WebSocket server`);
414
- const room = new RoomState(`view_${projectId}`);
447
+ const room = new RoomState(`view_${projectId}`, []);
448
+ const fileManager = new FileManager(srcDir);
449
+ const initialFiles = fileManager.listFiles();
450
+ room.files = initialFiles;
415
451
  room.connect(async () => {
416
452
  devSpinner.succeed("Project started in development mode!");
417
- const fileManager = new FileManager(srcDir);
418
- const initialFiles = fileManager.listFiles();
419
453
  room.updateState("view", initialFiles);
420
454
  fileManager.setOnFileChange((filePath, content) => {
421
- const es5 = (0, exports.compileES5)(content, filePath);
455
+ var _a;
456
+ const es5 = (_a = (0, exports.compileES5)(content, filePath)) !== null && _a !== void 0 ? _a : "";
422
457
  console.log(`File changed: ${filePath === null || filePath === void 0 ? void 0 : filePath.replace(srcDir, ".")}`);
423
- const updatedView = room.files.map((file) => {
458
+ const updatedFiles = room.files.map((file) => {
424
459
  if (path.join(srcDir, file.path) === filePath) {
425
460
  return { ...file, content, es5 };
426
461
  }
427
462
  return file;
428
463
  });
429
- room.updateState("view", updatedView);
464
+ room.files = updatedFiles;
465
+ room.updateState("view", updatedFiles);
430
466
  });
431
467
  // Fetch project info
432
468
  const projectInfo = getStoredProjectId(pwd);
@@ -445,7 +481,6 @@ commander_1.program
445
481
  return;
446
482
  }
447
483
  const projectType = project.data.type || "view";
448
- console.log("Project type:", projectType);
449
484
  if (projectType === "zalominiapp") {
450
485
  qrcode_terminal_1.default.generate(`https://zalo.me/s/3218692650896662017/player/${projectId}`, { small: true }, (qrcode) => {
451
486
  console.log("Scan this QR code to connect to the project:");
package/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
-
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import { isEqual, find } from "lodash";
3
4
  import { program } from "commander";
4
5
  import axios from "axios";
5
6
  import * as fs from "fs";
@@ -249,6 +250,10 @@ class FileManager {
249
250
  }
250
251
  }
251
252
 
253
+ interface FileContent {
254
+ // Define the structure of FileContent if needed
255
+ }
256
+
252
257
  class RoomState {
253
258
  private roomId: string;
254
259
  private state: RoomStateData;
@@ -256,16 +261,24 @@ class RoomState {
256
261
  private ws: WebSocket | null;
257
262
  private reconnectTimeout: NodeJS.Timeout | null;
258
263
  private reconnectAttempts: number;
264
+ private onChangeListeners: Array<{
265
+ key: string;
266
+ callback: (value: any) => void;
267
+ }>;
259
268
 
260
269
  public files: FileContent[] = [];
261
270
 
262
- constructor(roomId: string) {
271
+ constructor(
272
+ roomId: string,
273
+ onChangeListeners: Array<{ key: string; callback: (value: any) => void }>
274
+ ) {
263
275
  this.roomId = roomId;
264
276
  this.state = {};
265
277
  this.isConnected = false;
266
278
  this.ws = null;
267
279
  this.reconnectTimeout = null;
268
280
  this.reconnectAttempts = 0;
281
+ this.onChangeListeners = onChangeListeners;
269
282
  }
270
283
 
271
284
  connect(onConnected?: () => void): void {
@@ -279,36 +292,79 @@ class RoomState {
279
292
  `https://base.myworkbeast.com/ws?room=${this.roomId}`
280
293
  );
281
294
 
282
- this.ws.on("open", () => {
295
+ this.ws.onopen = () => {
296
+ console.log(`Connected to room: ${this.roomId}`);
283
297
  this.isConnected = true;
284
298
  this.reconnectAttempts = 0;
285
- onConnected?.();
286
- connected.succeed(`Connected to WebSocket server`);
287
- });
299
+ onConnected?.(); // Call the onConnected callback if provided
300
+ };
288
301
 
289
- this.ws.on("close", () => {
302
+ this.ws.onclose = () => {
303
+ console.log(`Disconnected from room: ${this.roomId}`);
290
304
  this.isConnected = false;
291
305
 
292
306
  this.reconnectAttempts++;
293
- connected.start(`Retry connection...`);
307
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
308
+ console.log(`Attempting to reconnect in ${delay / 1000}s...`);
294
309
 
295
- this.connect();
296
- });
310
+ this.reconnectTimeout = setTimeout(
311
+ () => this.connect(onConnected),
312
+ delay
313
+ );
314
+ };
297
315
 
298
- this.ws.on("message", (data: Buffer) => {
316
+ this.ws.on("message", (data: Buffer | string) => {
299
317
  try {
300
- const message: WebSocketMessage = JSON.parse(data.toString());
301
- if (message.type === "state" && message.state) {
302
- this.state = message.state;
303
- const view = this.state.view as Array<FileContent>;
304
- this.files = view;
318
+ const jsonStr = typeof data === "string" ? data : data.toString();
319
+ const parsedData = JSON.parse(jsonStr);
320
+ const includes = [
321
+ "view",
322
+ "coding",
323
+ "refresh_key",
324
+ "useActiveIdFocus",
325
+ "activeId",
326
+ "active_file",
327
+ "figma",
328
+ ];
329
+
330
+ let newState: RoomStateData;
331
+ let changedKeys;
332
+
333
+ if (parsedData.type === "state") {
334
+ // Full state (e.g., on initial connect)
335
+ newState = parsedData.state;
336
+ changedKeys = Object.keys(newState).filter(
337
+ (key) => !isEqual(newState[key], this.state[key])
338
+ );
339
+ } else if (parsedData.type === "update") {
340
+ // Delta update
341
+ const delta = parsedData.state;
342
+ newState = { ...this.state, ...delta };
343
+ changedKeys = Object.keys(delta).filter(
344
+ (key) => !isEqual(delta[key], this.state[key])
345
+ );
346
+ } else {
347
+ return;
348
+ }
349
+
350
+ const filteredChangedKeys = changedKeys.filter((key) =>
351
+ includes.includes(key)
352
+ );
353
+ if (filteredChangedKeys.length > 0) {
354
+ filteredChangedKeys.forEach((key) => {
355
+ const listener = find(this.onChangeListeners, (l) => l.key === key);
356
+ if (listener) {
357
+ listener.callback(newState[key]);
358
+ }
359
+ });
360
+ this.state = newState;
305
361
  }
306
362
  } catch (e) {
307
363
  console.error("Error processing message:", e);
308
364
  }
309
365
  });
310
366
 
311
- this.ws.on("error", (err: Error) => {
367
+ this.ws.on("error", (err) => {
312
368
  console.error("WebSocket error:", err);
313
369
  this.ws?.close();
314
370
  });
@@ -322,8 +378,6 @@ class RoomState {
322
378
  state: { [key]: value },
323
379
  })
324
380
  );
325
- } else {
326
- console.log("Cannot update state: Not connected");
327
381
  }
328
382
  }
329
383
 
@@ -332,7 +386,7 @@ class RoomState {
332
386
  clearTimeout(this.reconnectTimeout);
333
387
  }
334
388
  if (this.ws) {
335
- this.ws.removeAllListeners("close");
389
+ this.ws.onclose = null; // Prevent reconnect on intentional close
336
390
  this.ws.close();
337
391
  }
338
392
  }
@@ -346,7 +400,7 @@ class RoomState {
346
400
  }
347
401
  }
348
402
 
349
- program.version("0.0.14").description("Hapico CLI for project management");
403
+ program.version("0.0.16").description("Hapico CLI for project management");
350
404
 
351
405
  program
352
406
  .command("clone <id>")
@@ -431,7 +485,9 @@ program
431
485
  console.error("You need to login first. Use 'hapico login' command.");
432
486
  return;
433
487
  }
434
- const devSpinner = ora("Starting the project in development mode...").start();
488
+ const devSpinner = ora(
489
+ "Starting the project in development mode..."
490
+ ).start();
435
491
  const pwd = process.cwd();
436
492
  const srcDir = path.join(pwd, "src");
437
493
  if (!fs.existsSync(srcDir)) {
@@ -492,26 +548,27 @@ program
492
548
  }
493
549
 
494
550
  console.log(`Connecting to WebSocket server`);
495
- const room = new RoomState(`view_${projectId}`);
551
+ const room = new RoomState(`view_${projectId}`, []);
552
+ const fileManager = new FileManager(srcDir);
553
+ const initialFiles = fileManager.listFiles();
554
+ room.files = initialFiles;
496
555
 
497
556
  room.connect(async () => {
498
557
  devSpinner.succeed("Project started in development mode!");
499
558
 
500
- const fileManager = new FileManager(srcDir);
501
-
502
- const initialFiles = fileManager.listFiles();
503
559
  room.updateState("view", initialFiles);
504
560
 
505
561
  fileManager.setOnFileChange((filePath, content) => {
506
- const es5 = compileES5(content, filePath);
562
+ const es5 = compileES5(content, filePath) ?? "";
507
563
  console.log(`File changed: ${filePath?.replace(srcDir, ".")}`);
508
- const updatedView = room.files.map((file) => {
564
+ const updatedFiles = room.files.map((file) => {
509
565
  if (path.join(srcDir, file.path) === filePath) {
510
566
  return { ...file, content, es5 };
511
567
  }
512
568
  return file;
513
569
  });
514
- room.updateState("view", updatedView);
570
+ room.files = updatedFiles;
571
+ room.updateState("view", updatedFiles);
515
572
  });
516
573
 
517
574
  // Fetch project info
@@ -539,8 +596,6 @@ program
539
596
 
540
597
  const projectType = project.data.type || "view";
541
598
 
542
- console.log("Project type:", projectType);
543
-
544
599
  if (projectType === "zalominiapp") {
545
600
  QRCode.generate(
546
601
  `https://zalo.me/s/3218692650896662017/player/${projectId}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hapico/cli",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "A simple CLI tool for project management",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -23,6 +23,7 @@
23
23
  "@babel/standalone": "^7.28.2",
24
24
  "axios": "^1.11.0",
25
25
  "commander": "^14.0.0",
26
+ "lodash": "^4.17.21",
26
27
  "open": "^10.2.0",
27
28
  "ora": "^8.2.0",
28
29
  "qrcode-terminal": "^0.12.0",
@@ -32,6 +33,7 @@
32
33
  "devDependencies": {
33
34
  "@types/babel__standalone": "^7.1.9",
34
35
  "@types/commander": "^2.12.5",
36
+ "@types/lodash": "^4.17.20",
35
37
  "@types/node": "^24.1.0",
36
38
  "@types/qrcode-terminal": "^0.12.2",
37
39
  "@types/unzipper": "^0.10.11",