* Create directory recursively * @param {string} parent * @param {Array | string} dir
(parent, dir, shouldBeDirAtEnd)
| 288 | * @param {Array<string> | string} dir |
| 289 | */ |
| 290 | async function createFileRecursive(parent, dir, shouldBeDirAtEnd) { |
| 291 | let wantDirEnd = !!shouldBeDirAtEnd; |
| 292 | /** @type {string[]} */ |
| 293 | let parts; |
| 294 | if (typeof dir === "string") { |
| 295 | if (dir.endsWith("/")) wantDirEnd = true; |
| 296 | dir = dir.replace(/\\/g, "/"); |
| 297 | parts = dir.split("/"); |
| 298 | } else { |
| 299 | parts = dir; |
| 300 | } |
| 301 | parts = parts.filter((d) => d); |
| 302 | const cd = parts.shift(); |
| 303 | if (!cd) return; |
| 304 | const newParent = Url.join(parent, cd); |
| 305 | |
| 306 | const isLast = parts.length === 0; |
| 307 | const needDir = !isLast || wantDirEnd; |
| 308 | if (!(await fsOperation(newParent).exists())) { |
| 309 | if (needDir) { |
| 310 | try { |
| 311 | await fsOperation(parent).createDirectory(cd); |
| 312 | } catch (e) { |
| 313 | // If another concurrent task created it, consider it fine |
| 314 | if (!(await fsOperation(newParent).exists())) throw e; |
| 315 | } |
| 316 | } else { |
| 317 | try { |
| 318 | await fsOperation(parent).createFile(cd); |
| 319 | } catch (e) { |
| 320 | if (!(await fsOperation(newParent).exists())) throw e; |
| 321 | } |
| 322 | } |
| 323 | } |
| 324 | if (parts.length) { |
| 325 | await createFileRecursive(newParent, parts, wantDirEnd); |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | /** |
| 330 | * Sanitize zip entry path to ensure it's relative and safe under pluginDir |
no test coverage detected