@hapico/cli 0.0.19 → 0.0.20
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 +209 -54
- package/dist/index.js +209 -54
- package/index.ts +285 -67
- package/package.json +1 -1
- package/test.tsx +0 -0
package/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
-
import { isEqual, find } from "lodash";
|
|
3
|
+
import { isEqual, find, map } from "lodash";
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
import * as fs from "fs";
|
|
@@ -15,7 +15,7 @@ import { randomUUID } from "crypto";
|
|
|
15
15
|
import { exec } from "child_process";
|
|
16
16
|
import { promisify } from "util";
|
|
17
17
|
import chalk from "chalk";
|
|
18
|
-
import pako from
|
|
18
|
+
import pako from "pako";
|
|
19
19
|
|
|
20
20
|
// Promisify exec for async usage
|
|
21
21
|
const execPromise = promisify(exec);
|
|
@@ -74,14 +74,25 @@ const saveProjectId = (projectDir: string, id: string) => {
|
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
// Function to get stored project ID
|
|
77
|
-
const getStoredProjectId = (
|
|
77
|
+
const getStoredProjectId = (
|
|
78
|
+
projectDir: string
|
|
79
|
+
): {
|
|
80
|
+
projectId: string | null;
|
|
81
|
+
replicate?: string[];
|
|
82
|
+
} => {
|
|
78
83
|
const configFile = path.join(projectDir, "hapico.config.json");
|
|
79
84
|
if (fs.existsSync(configFile)) {
|
|
80
85
|
const data = fs.readFileSync(configFile, { encoding: "utf8" });
|
|
81
86
|
const json = JSON.parse(data);
|
|
82
|
-
return
|
|
87
|
+
return {
|
|
88
|
+
projectId: json.projectId || null,
|
|
89
|
+
replicate: json.replicate || [],
|
|
90
|
+
};
|
|
83
91
|
}
|
|
84
|
-
return
|
|
92
|
+
return {
|
|
93
|
+
projectId: null,
|
|
94
|
+
replicate: [],
|
|
95
|
+
};
|
|
85
96
|
};
|
|
86
97
|
|
|
87
98
|
// Khởi tạo cache bằng Map để lưu trữ kết quả compile
|
|
@@ -95,7 +106,7 @@ interface FileContent {
|
|
|
95
106
|
|
|
96
107
|
interface ApiResponse {
|
|
97
108
|
data: {
|
|
98
|
-
|
|
109
|
+
code: string;
|
|
99
110
|
type: "view" | "zalominiapp" | string;
|
|
100
111
|
dbCode: string;
|
|
101
112
|
db_code: string;
|
|
@@ -111,6 +122,14 @@ interface WebSocketMessage {
|
|
|
111
122
|
state?: RoomStateData;
|
|
112
123
|
}
|
|
113
124
|
|
|
125
|
+
export const tryJSONParse = (str: string): any => {
|
|
126
|
+
try {
|
|
127
|
+
return JSON.parse(str);
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
114
133
|
export const compileES5 = (code: string, filePath?: string) => {
|
|
115
134
|
if (filePath && !filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
|
|
116
135
|
return code;
|
|
@@ -131,7 +150,8 @@ export const compileES5 = (code: string, filePath?: string) => {
|
|
|
131
150
|
|
|
132
151
|
compileCache.set(code, result.code || "");
|
|
133
152
|
return result.code;
|
|
134
|
-
} catch (error) {
|
|
153
|
+
} catch (error: any) {
|
|
154
|
+
console.log(chalk.red(`Error compiling code: ${error?.message}`));
|
|
135
155
|
return "";
|
|
136
156
|
}
|
|
137
157
|
};
|
|
@@ -303,12 +323,10 @@ class RoomState {
|
|
|
303
323
|
clearTimeout(this.reconnectTimeout);
|
|
304
324
|
}
|
|
305
325
|
|
|
306
|
-
this.ws = new WebSocket(
|
|
307
|
-
`wss://ws3.myworkbeast.com/ws?room=${this.roomId}`
|
|
308
|
-
);
|
|
326
|
+
this.ws = new WebSocket(`wss://ws3.myworkbeast.com/ws?room=${this.roomId}`);
|
|
309
327
|
|
|
310
328
|
// Set binaryType to 'arraybuffer' to handle binary data
|
|
311
|
-
this.ws.binaryType =
|
|
329
|
+
this.ws.binaryType = "arraybuffer";
|
|
312
330
|
|
|
313
331
|
this.ws.on("open", () => {
|
|
314
332
|
console.log(chalk.green(`Connected to room: ${this.roomId}`));
|
|
@@ -337,11 +355,11 @@ class RoomState {
|
|
|
337
355
|
try {
|
|
338
356
|
let jsonStr: string;
|
|
339
357
|
if (data instanceof ArrayBuffer) {
|
|
340
|
-
jsonStr = pako.inflate(data, { to:
|
|
358
|
+
jsonStr = pako.inflate(data, { to: "string" });
|
|
341
359
|
} else if (typeof data === "string") {
|
|
342
360
|
jsonStr = data;
|
|
343
361
|
} else if (Buffer.isBuffer(data)) {
|
|
344
|
-
jsonStr = pako.inflate(data, { to:
|
|
362
|
+
jsonStr = pako.inflate(data, { to: "string" });
|
|
345
363
|
} else {
|
|
346
364
|
jsonStr = data.toString(); // Fallback nếu không nén
|
|
347
365
|
}
|
|
@@ -429,7 +447,7 @@ class RoomState {
|
|
|
429
447
|
}
|
|
430
448
|
}
|
|
431
449
|
|
|
432
|
-
program.version("0.0.
|
|
450
|
+
program.version("0.0.20").description("Hapico CLI for project management");
|
|
433
451
|
|
|
434
452
|
program
|
|
435
453
|
.command("clone <id>")
|
|
@@ -455,7 +473,12 @@ program
|
|
|
455
473
|
const response: ApiResponse = await axios.get(
|
|
456
474
|
`https://base.myworkbeast.com/api/views/${id}`
|
|
457
475
|
);
|
|
458
|
-
|
|
476
|
+
const code = response?.data?.code;
|
|
477
|
+
const decompressedCode = pako.inflate(
|
|
478
|
+
Uint8Array.from(atob(code), (c) => c.charCodeAt(0)),
|
|
479
|
+
{ to: "string" }
|
|
480
|
+
);
|
|
481
|
+
files = tryJSONParse(decompressedCode)?.files || [];
|
|
459
482
|
apiSpinner.succeed(chalk.green("Project data fetched successfully!"));
|
|
460
483
|
|
|
461
484
|
const templateSpinner: Ora = ora(
|
|
@@ -578,7 +601,7 @@ program
|
|
|
578
601
|
const info = JSON.stringify({
|
|
579
602
|
id: sessionId,
|
|
580
603
|
createdAt: new Date().toISOString(),
|
|
581
|
-
viewId: getStoredProjectId(pwd),
|
|
604
|
+
viewId: getStoredProjectId(pwd)?.projectId,
|
|
582
605
|
});
|
|
583
606
|
|
|
584
607
|
// Convert info to base64
|
|
@@ -596,12 +619,47 @@ program
|
|
|
596
619
|
const room = new RoomState(`view_${projectId}`, []);
|
|
597
620
|
const fileManager = new FileManager(srcDir);
|
|
598
621
|
const initialFiles = fileManager.listFiles();
|
|
599
|
-
|
|
622
|
+
// get tsconfig.json
|
|
623
|
+
const tsconfigPath = path.join(srcDir, "..", "tsconfig.json");
|
|
624
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
625
|
+
const content = fs.readFileSync(tsconfigPath, { encoding: "utf8" });
|
|
626
|
+
initialFiles.push({
|
|
627
|
+
path: "./tsconfig.json",
|
|
628
|
+
content,
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
// Remove All binary files
|
|
632
|
+
const supportExtensions = [
|
|
633
|
+
".ts",
|
|
634
|
+
".tsx",
|
|
635
|
+
".js",
|
|
636
|
+
".jsx",
|
|
637
|
+
".json",
|
|
638
|
+
".css",
|
|
639
|
+
".html",
|
|
640
|
+
".env",
|
|
641
|
+
".env.local",
|
|
642
|
+
".env.development",
|
|
643
|
+
".env.production",
|
|
644
|
+
".jsonc",
|
|
645
|
+
".yml",
|
|
646
|
+
".yaml",
|
|
647
|
+
".md",
|
|
648
|
+
".markdown",
|
|
649
|
+
".txt",
|
|
650
|
+
".xml",
|
|
651
|
+
".config",
|
|
652
|
+
];
|
|
653
|
+
const filteredFiles = initialFiles.filter((file) => {
|
|
654
|
+
return supportExtensions.some((ext) => file.path.endsWith(ext));
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
room.files = filteredFiles;
|
|
600
658
|
|
|
601
659
|
room.connect(async () => {
|
|
602
660
|
devSpinner.succeed(chalk.green("Project started in development mode!"));
|
|
603
661
|
|
|
604
|
-
room.updateState("view",
|
|
662
|
+
room.updateState("view", filteredFiles);
|
|
605
663
|
|
|
606
664
|
fileManager.setOnFileChange((filePath, content) => {
|
|
607
665
|
const es5 = compileES5(content, filePath) ?? "";
|
|
@@ -619,8 +677,8 @@ program
|
|
|
619
677
|
});
|
|
620
678
|
|
|
621
679
|
// Fetch project info
|
|
622
|
-
const
|
|
623
|
-
if (!
|
|
680
|
+
const store = getStoredProjectId(pwd);
|
|
681
|
+
if (!store.projectId) {
|
|
624
682
|
console.error(
|
|
625
683
|
chalk.red(
|
|
626
684
|
"✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."
|
|
@@ -629,7 +687,7 @@ program
|
|
|
629
687
|
return;
|
|
630
688
|
}
|
|
631
689
|
const project = await axios.get(
|
|
632
|
-
`https://base.myworkbeast.com/api/views/${
|
|
690
|
+
`https://base.myworkbeast.com/api/views/${store.projectId}`,
|
|
633
691
|
{
|
|
634
692
|
headers: {
|
|
635
693
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -672,23 +730,16 @@ program
|
|
|
672
730
|
});
|
|
673
731
|
|
|
674
732
|
// Hàm tái sử dụng để push mã nguồn lên server
|
|
675
|
-
async function pushProject(spinner: Ora): Promise<boolean> {
|
|
733
|
+
async function pushProject(spinner: Ora, projectId: string): Promise<boolean> {
|
|
676
734
|
const data = getStoredToken();
|
|
677
735
|
const { accessToken } = data || {};
|
|
678
736
|
if (!accessToken) {
|
|
679
|
-
spinner.fail(chalk.red("✗ You need to login first. Use 'hapico login' command."));
|
|
680
|
-
return false;
|
|
681
|
-
}
|
|
682
|
-
const pwd = process.cwd();
|
|
683
|
-
const projectId = getStoredProjectId(pwd);
|
|
684
|
-
if (!projectId) {
|
|
685
737
|
spinner.fail(
|
|
686
|
-
chalk.red(
|
|
687
|
-
"✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."
|
|
688
|
-
)
|
|
738
|
+
chalk.red("✗ You need to login first. Use 'hapico login' command.")
|
|
689
739
|
);
|
|
690
740
|
return false;
|
|
691
741
|
}
|
|
742
|
+
const pwd = process.cwd();
|
|
692
743
|
const srcDir: string = path.join(pwd, "src");
|
|
693
744
|
if (!fs.existsSync(srcDir)) {
|
|
694
745
|
spinner.fail(
|
|
@@ -701,17 +752,35 @@ async function pushProject(spinner: Ora): Promise<boolean> {
|
|
|
701
752
|
const fileManager = new FileManager(srcDir);
|
|
702
753
|
const files = fileManager.listFiles();
|
|
703
754
|
|
|
704
|
-
//
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
755
|
+
// Supported files
|
|
756
|
+
const SUPPORT_FILES = [
|
|
757
|
+
"./.env",
|
|
758
|
+
"./.env.local",
|
|
759
|
+
"./.env.development",
|
|
760
|
+
"./.env.production",
|
|
761
|
+
"./package.json",
|
|
762
|
+
"./tsconfig.json",
|
|
763
|
+
];
|
|
764
|
+
|
|
765
|
+
// Include supported files
|
|
766
|
+
SUPPORT_FILES.forEach((relativePath) => {
|
|
767
|
+
const fullPath = path.join(pwd, relativePath);
|
|
768
|
+
if (fs.existsSync(fullPath)) {
|
|
769
|
+
console.log(
|
|
770
|
+
chalk.green(
|
|
771
|
+
`Including ${relativePath} in push for project ${projectId}.`
|
|
772
|
+
)
|
|
773
|
+
);
|
|
774
|
+
const content = fs.readFileSync(fullPath, { encoding: "utf8" });
|
|
775
|
+
files.push({
|
|
776
|
+
path: relativePath,
|
|
777
|
+
content,
|
|
778
|
+
es5: compileES5(content, fullPath) ?? "",
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
spinner.text = `Pushing project source code to server for project ${projectId}...`;
|
|
783
|
+
|
|
715
784
|
const apiUrl = `https://base.myworkbeast.com/api/views/${projectId}`;
|
|
716
785
|
try {
|
|
717
786
|
await axios.put(
|
|
@@ -730,7 +799,11 @@ async function pushProject(spinner: Ora): Promise<boolean> {
|
|
|
730
799
|
);
|
|
731
800
|
return true;
|
|
732
801
|
} catch (error) {
|
|
733
|
-
spinner.fail(
|
|
802
|
+
spinner.fail(
|
|
803
|
+
chalk.red(
|
|
804
|
+
`✗ Error saving project ${projectId}: ${(error as Error).message}`
|
|
805
|
+
)
|
|
806
|
+
);
|
|
734
807
|
return false;
|
|
735
808
|
}
|
|
736
809
|
}
|
|
@@ -742,10 +815,49 @@ program
|
|
|
742
815
|
const saveSpinner: Ora = ora(
|
|
743
816
|
chalk.blue("Saving project source code...")
|
|
744
817
|
).start();
|
|
745
|
-
const
|
|
746
|
-
|
|
818
|
+
const pwd = process.cwd();
|
|
819
|
+
const { projectId, replicate } = getStoredProjectId(pwd);
|
|
820
|
+
|
|
821
|
+
if (!projectId) {
|
|
822
|
+
saveSpinner.fail(
|
|
823
|
+
chalk.red(
|
|
824
|
+
"✗ Project ID not found. Please ensure hapico.config.json exists in the project directory."
|
|
825
|
+
)
|
|
826
|
+
);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Push to the main project
|
|
831
|
+
const mainSuccess = await pushProject(saveSpinner, projectId);
|
|
832
|
+
let allSuccess = mainSuccess;
|
|
833
|
+
|
|
834
|
+
// Push to replicated projects if replicate array exists
|
|
835
|
+
if (replicate && Array.isArray(replicate) && replicate.length > 0) {
|
|
836
|
+
saveSpinner.text = chalk.blue("Pushing to replicated projects...");
|
|
837
|
+
for (const repId of replicate) {
|
|
838
|
+
const success = await pushProject(saveSpinner, repId);
|
|
839
|
+
if (!success) {
|
|
840
|
+
allSuccess = false;
|
|
841
|
+
console.warn(
|
|
842
|
+
chalk.yellow(
|
|
843
|
+
`⚠ Failed to push to replicated project ${repId}. Continuing...`
|
|
844
|
+
)
|
|
845
|
+
);
|
|
846
|
+
} else {
|
|
847
|
+
console.log(
|
|
848
|
+
chalk.green(`✓ Successfully pushed to replicated project ${repId}.`)
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
if (allSuccess) {
|
|
747
855
|
saveSpinner.succeed(
|
|
748
|
-
chalk.green("Project source code saved successfully!")
|
|
856
|
+
chalk.green("Project source code saved successfully to all projects!")
|
|
857
|
+
);
|
|
858
|
+
} else {
|
|
859
|
+
saveSpinner.warn(
|
|
860
|
+
chalk.yellow("Project source code saved with some errors.")
|
|
749
861
|
);
|
|
750
862
|
}
|
|
751
863
|
});
|
|
@@ -838,7 +950,7 @@ program
|
|
|
838
950
|
return;
|
|
839
951
|
}
|
|
840
952
|
const pwd: string = process.cwd();
|
|
841
|
-
const projectId = getStoredProjectId(pwd);
|
|
953
|
+
const { projectId } = getStoredProjectId(pwd);
|
|
842
954
|
if (!projectId) {
|
|
843
955
|
console.error(
|
|
844
956
|
chalk.red(
|
|
@@ -852,15 +964,22 @@ program
|
|
|
852
964
|
).start();
|
|
853
965
|
try {
|
|
854
966
|
const response: ApiResponse = await axios.get(
|
|
855
|
-
`https://base.myworkbeast.com/api/views/${projectId}`,
|
|
967
|
+
`https://base.myworkbeast.com/api/views/v3/${projectId}`,
|
|
856
968
|
{
|
|
857
969
|
headers: {
|
|
858
|
-
Authorization: `Bearer ${token}`,
|
|
970
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
859
971
|
"Content-Type": "application/json",
|
|
860
972
|
},
|
|
861
973
|
}
|
|
862
974
|
);
|
|
863
|
-
|
|
975
|
+
|
|
976
|
+
const code = response?.data?.code;
|
|
977
|
+
const decompressedCode = pako.inflate(
|
|
978
|
+
Uint8Array.from(atob(code), (c) => c.charCodeAt(0)),
|
|
979
|
+
{ to: "string" }
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
const files: FileContent[] = tryJSONParse(decompressedCode)?.files || [];
|
|
864
983
|
const fileManager = new FileManager(path.join(pwd, "src"));
|
|
865
984
|
fileManager.syncFiles(files);
|
|
866
985
|
apiSpinner.succeed(chalk.green("Project files updated successfully!"));
|
|
@@ -999,7 +1118,9 @@ program
|
|
|
999
1118
|
migrationsDir,
|
|
1000
1119
|
`${index + 1}_migration_${timestamp}.sql`
|
|
1001
1120
|
);
|
|
1002
|
-
fs.writeFileSync(filename, (migration as any).sql, {
|
|
1121
|
+
fs.writeFileSync(filename, (migration as any).sql, {
|
|
1122
|
+
encoding: "utf8",
|
|
1123
|
+
});
|
|
1003
1124
|
});
|
|
1004
1125
|
|
|
1005
1126
|
console.log(chalk.green("✓ Migrations saved successfully!"));
|
|
@@ -1011,7 +1132,6 @@ program
|
|
|
1011
1132
|
).start();
|
|
1012
1133
|
|
|
1013
1134
|
try {
|
|
1014
|
-
files = response.data.files || [];
|
|
1015
1135
|
apiSpinner.succeed(chalk.green("Project data fetched successfully!"));
|
|
1016
1136
|
|
|
1017
1137
|
const saveSpinner: Ora = ora(
|
|
@@ -1232,17 +1352,12 @@ program
|
|
|
1232
1352
|
|
|
1233
1353
|
// hapico publish
|
|
1234
1354
|
program.command("publish").action(async () => {
|
|
1235
|
-
const publishSpinner: Ora = ora(
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
if (!pushSuccess) {
|
|
1239
|
-
return; // Dừng nếu push thất bại
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
// Bước 2: Gọi API để publish
|
|
1243
|
-
const { accessToken } = getStoredToken();
|
|
1355
|
+
const publishSpinner: Ora = ora(
|
|
1356
|
+
chalk.blue("Publishing to Hapico...")
|
|
1357
|
+
).start();
|
|
1244
1358
|
const pwd = process.cwd();
|
|
1245
|
-
const projectId = getStoredProjectId(pwd);
|
|
1359
|
+
const { projectId, replicate } = getStoredProjectId(pwd);
|
|
1360
|
+
|
|
1246
1361
|
if (!projectId) {
|
|
1247
1362
|
publishSpinner.fail(
|
|
1248
1363
|
chalk.red(
|
|
@@ -1251,11 +1366,42 @@ program.command("publish").action(async () => {
|
|
|
1251
1366
|
);
|
|
1252
1367
|
return;
|
|
1253
1368
|
}
|
|
1369
|
+
|
|
1370
|
+
// Step 1: Push source code to main project and replicas
|
|
1371
|
+
const pushSuccess = await pushProject(publishSpinner, projectId);
|
|
1372
|
+
if (!pushSuccess) {
|
|
1373
|
+
return; // Stop if push to main project fails
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// Push to replicated projects
|
|
1377
|
+
let allPushSuccess = true;
|
|
1378
|
+
if (replicate && Array.isArray(replicate) && replicate.length > 0) {
|
|
1379
|
+
publishSpinner.text = chalk.blue("Pushing to replicated projects...");
|
|
1380
|
+
for (const repId of replicate) {
|
|
1381
|
+
const success = await pushProject(publishSpinner, repId);
|
|
1382
|
+
if (!success) {
|
|
1383
|
+
allPushSuccess = false;
|
|
1384
|
+
console.warn(
|
|
1385
|
+
chalk.yellow(
|
|
1386
|
+
`⚠ Failed to push to replicated project ${repId}. Continuing...`
|
|
1387
|
+
)
|
|
1388
|
+
);
|
|
1389
|
+
} else {
|
|
1390
|
+
console.log(
|
|
1391
|
+
chalk.green(`✓ Successfully pushed to replicated project ${repId}.`)
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// Step 2: Publish main project
|
|
1398
|
+
const { accessToken } = getStoredToken();
|
|
1254
1399
|
const apiUrl = "https://base.myworkbeast.com/api/views/publish";
|
|
1400
|
+
let allPublishSuccess = true;
|
|
1255
1401
|
try {
|
|
1256
1402
|
await axios.post(
|
|
1257
1403
|
apiUrl,
|
|
1258
|
-
{ view_id: parseInt(projectId, 10) },
|
|
1404
|
+
{ view_id: parseInt(projectId, 10) },
|
|
1259
1405
|
{
|
|
1260
1406
|
headers: {
|
|
1261
1407
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -1263,11 +1409,83 @@ program.command("publish").action(async () => {
|
|
|
1263
1409
|
},
|
|
1264
1410
|
}
|
|
1265
1411
|
);
|
|
1266
|
-
publishSpinner.succeed(
|
|
1412
|
+
publishSpinner.succeed(
|
|
1413
|
+
chalk.green(`Project ${projectId} published successfully!`)
|
|
1414
|
+
);
|
|
1267
1415
|
} catch (error) {
|
|
1268
|
-
|
|
1269
|
-
publishSpinner.fail(
|
|
1416
|
+
allPublishSuccess = false;
|
|
1417
|
+
publishSpinner.fail(
|
|
1418
|
+
chalk.red(
|
|
1419
|
+
`✗ Error publishing project ${projectId}: ${(error as Error).message}`
|
|
1420
|
+
)
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// Step 3: Publish replicated projects
|
|
1425
|
+
if (replicate && Array.isArray(replicate) && replicate.length > 0) {
|
|
1426
|
+
publishSpinner.text = chalk.blue("Publishing replicated projects...");
|
|
1427
|
+
for (const repId of replicate) {
|
|
1428
|
+
try {
|
|
1429
|
+
await axios.post(
|
|
1430
|
+
apiUrl,
|
|
1431
|
+
{ view_id: parseInt(repId, 10) },
|
|
1432
|
+
{
|
|
1433
|
+
headers: {
|
|
1434
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1435
|
+
"Content-Type": "application/json",
|
|
1436
|
+
},
|
|
1437
|
+
}
|
|
1438
|
+
);
|
|
1439
|
+
console.log(
|
|
1440
|
+
chalk.green(`✓ Successfully published replicated project ${repId}.`)
|
|
1441
|
+
);
|
|
1442
|
+
} catch (error) {
|
|
1443
|
+
allPublishSuccess = false;
|
|
1444
|
+
console.warn(
|
|
1445
|
+
chalk.yellow(
|
|
1446
|
+
`⚠ Error publishing replicated project ${repId}: ${(error as Error).message}`
|
|
1447
|
+
)
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
if (allPushSuccess && allPublishSuccess) {
|
|
1454
|
+
publishSpinner.succeed(chalk.green("All projects published successfully!"));
|
|
1455
|
+
} else {
|
|
1456
|
+
publishSpinner.warn(chalk.yellow("Publishing completed with some errors."));
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
program.command("mirror").action(() => {
|
|
1461
|
+
console.log(chalk.cyan("🌐 Starting mirror mode..."));
|
|
1462
|
+
const pwd = process.cwd();
|
|
1463
|
+
const srcDir = path.join(pwd, "src");
|
|
1464
|
+
if (!fs.existsSync(srcDir)) {
|
|
1465
|
+
console.error(
|
|
1466
|
+
chalk.red(
|
|
1467
|
+
"✗ Source directory 'src' does not exist. Please clone a project first."
|
|
1468
|
+
)
|
|
1469
|
+
);
|
|
1470
|
+
return;
|
|
1270
1471
|
}
|
|
1472
|
+
|
|
1473
|
+
const fileManager = new FileManager(srcDir);
|
|
1474
|
+
const initialFiles = fileManager.listFiles();
|
|
1475
|
+
|
|
1476
|
+
// Lấy danh sách file và viết ra 1 file .txt
|
|
1477
|
+
let content = ``;
|
|
1478
|
+
map(initialFiles, (file) => {
|
|
1479
|
+
content += `\`\`\`typescript
|
|
1480
|
+
// Path: ${file.path}
|
|
1481
|
+
${file.content}
|
|
1482
|
+
|
|
1483
|
+
\`\`\`
|
|
1484
|
+
`;
|
|
1485
|
+
});
|
|
1486
|
+
const outputFile = path.join(pwd, "hapico_files.txt");
|
|
1487
|
+
fs.writeFileSync(outputFile, content, { encoding: "utf8" });
|
|
1488
|
+
console.log(chalk.green(`✓ File list saved to ${outputFile}`));
|
|
1271
1489
|
});
|
|
1272
1490
|
|
|
1273
|
-
program.parse(process.argv);
|
|
1491
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
package/test.tsx
ADDED
|
File without changes
|