]> Git Repo - pico-vscode.git/blob - src/utils/downloadHelpers.mts
Update dependencies + auto-format
[pico-vscode.git] / src / utils / downloadHelpers.mts
1 import { readdirSync, renameSync, rmdirSync, statSync } from "fs";
2 import { dirname, join } from "path";
3 import { join as joinPosix } from "path/posix";
4 import Logger from "../logger.mjs";
5 import { exec } from "child_process";
6 import AdmZip from "adm-zip";
7 import { request } from "https";
8 import { fileURLToPath } from "url";
9
10 export const CURRENT_DATA_VERSION = "0.15.0";
11
12 export function getDataRoot(): string {
13   return joinPosix(
14     dirname(fileURLToPath(import.meta.url)).replaceAll("\\", "/"),
15     "..",
16     "data",
17     CURRENT_DATA_VERSION
18   );
19 }
20
21 export function tryUnzipFiles(
22   zipFilePath: string,
23   targetDirectory: string
24 ): boolean {
25   let success = true;
26   const zip = new AdmZip(zipFilePath);
27   const zipEntries = zip.getEntries();
28   zipEntries.forEach(function (zipEntry) {
29     if (!zipEntry.isDirectory) {
30       try {
31         zip.extractEntryTo(zipEntry, targetDirectory, true, true, true);
32       } catch (error) {
33         Logger.log(
34           `Error extracting archive file: ${
35             error instanceof Error ? error.message : (error as string)
36           }`
37         );
38         success = false;
39       }
40     }
41   });
42
43   return success;
44 }
45
46 export function unzipFile(
47   zipFilePath: string,
48   targetDirectory: string,
49   enforceSuccess: boolean = false
50 ): boolean {
51   try {
52     if (enforceSuccess) {
53       const zip = new AdmZip(zipFilePath);
54       zip.extractAllTo(targetDirectory, true, true);
55     } else {
56       tryUnzipFiles(zipFilePath, targetDirectory);
57     }
58
59     // TODO: improve this
60     const targetDirContents = readdirSync(targetDirectory);
61     const subfolderPath =
62       targetDirContents.length === 1
63         ? join(targetDirectory, targetDirContents[0])
64         : "";
65     if (
66       targetDirContents.length === 1 &&
67       statSync(subfolderPath).isDirectory()
68     ) {
69       // Move all files and folders from the subfolder to targetDirectory
70       readdirSync(subfolderPath).forEach(item => {
71         const itemPath = join(subfolderPath, item);
72         const newItemPath = join(targetDirectory, item);
73
74         // Use fs.renameSync to move the item
75         renameSync(itemPath, newItemPath);
76       });
77
78       // Remove the empty subfolder
79       rmdirSync(subfolderPath);
80     }
81
82     return true;
83   } catch (error) {
84     Logger.log(
85       `Error extracting archive file: ${
86         error instanceof Error ? error.message : (error as string)
87       }`
88     );
89
90     return false;
91   }
92 }
93
94 /**
95  * Extracts a .xz file using the 'tar' command.
96  *
97  * Also supports tar.gz files.
98  *
99  * Linux and macOS only.
100  *
101  * @param xzFilePath
102  * @param targetDirectory
103  * @returns
104  */
105 export async function unxzFile(
106   xzFilePath: string,
107   targetDirectory: string
108 ): Promise<boolean> {
109   if (process.platform === "win32") {
110     return false;
111   }
112
113   return new Promise<boolean>(resolve => {
114     try {
115       // Construct the command to extract the .xz file using the 'tar' command
116       // -J option is redundant in modern versions of tar, but it's still good for compatibility
117       const command = `tar -x${
118         xzFilePath.endsWith(".xz") ? "J" : "z"
119       }f "${xzFilePath}" -C "${targetDirectory}"`;
120
121       // Execute the 'tar' command in the shell
122       exec(command, error => {
123         if (error) {
124           Logger.log(`Error extracting archive file: ${error?.message}`);
125           resolve(false);
126         } else {
127           const targetDirContents = readdirSync(targetDirectory);
128           const subfolderPath =
129             targetDirContents.length === 1
130               ? join(targetDirectory, targetDirContents[0])
131               : "";
132           if (
133             targetDirContents.length === 1 &&
134             statSync(subfolderPath).isDirectory()
135           ) {
136             // Move all files and folders from the subfolder to targetDirectory
137             readdirSync(subfolderPath).forEach(item => {
138               const itemPath = join(subfolderPath, item);
139               const newItemPath = join(targetDirectory, item);
140
141               // Use fs.renameSync to move the item
142               renameSync(itemPath, newItemPath);
143             });
144
145             // Remove the empty subfolder
146             rmdirSync(subfolderPath);
147           }
148
149           Logger.log(`Extracted archive file: ${xzFilePath}`);
150           resolve(true);
151         }
152       });
153     } catch {
154       resolve(false);
155     }
156   });
157 }
158
159 /**
160  * Checks if the internet connection is available (at least to pages.github.com).
161  *
162  * @returns True if the internet connection is available, false otherwise.
163  */
164 export async function isInternetConnected(): Promise<boolean> {
165   return new Promise<boolean>(resolve => {
166     const options = {
167       host: "pages.github.com",
168       port: 443,
169       path: "/?",
170       method: "HEAD",
171       headers: {
172         // eslint-disable-next-line @typescript-eslint/naming-convention
173         Host: "pages.github.com",
174       },
175     };
176
177     const req = request(options, res => {
178       resolve(res.statusCode === 200);
179     });
180
181     req.on("error", () => {
182       resolve(false);
183     });
184
185     req.end();
186   });
187 }
This page took 0.04293 seconds and 4 git commands to generate.