GeezerVison is showing github.com — fs: add directory autodetection to fsPromises.symlink() · nodejs/node@a4fa526
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/fs.md +12-3Lines changed: 12 additions & 3 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1463,18 +1463,27 @@ changes: | 1463 | 1463 |
| 1464 | 1464 | <!-- YAML | 1465 | 1465 | added: v10.0.0 | | 1466 | +changes: | | 1467 | + - version: REPLACEME | | 1468 | + pr-url: https://github.com/nodejs/node/pull/42894 | | 1469 | + description: If the `type` argument is `null` or omitted, Node.js will | | 1470 | + autodetect `target` type and automatically | | 1471 | + select `dir` or `file`. | | 1472 | +
| 1466 | 1473 | --> | 1467 | 1474 |
| 1468 | 1475 | * `target` {string|Buffer|URL} | 1469 | 1476 | * `path` {string|Buffer|URL} | 1470 | | -* `type` {string} **Default:** `'file'` | | 1477 | +* `type` {string|null} **Default:** `null` | 1471 | 1478 | * Returns: {Promise} Fulfills with `undefined` upon success. | 1472 | 1479 |
| 1473 | 1480 | Creates a symbolic link. | 1474 | 1481 |
| 1475 | 1482 | The `type` argument is only used on Windows platforms and can be one of `'dir'`, | 1476 | | -`'file'`, or `'junction'`. Windows junction points require the destination path | 1477 | | -to be absolute. When using `'junction'`, the `target` argument will | | 1483 | +`'file'`, or `'junction'`. If the `type` argument is not a string, Node.js will | | 1484 | +autodetect `target` type and use `'file'` or `'dir'`. If the `target` does not | | 1485 | +exist, `'file'` will be used. Windows junction points require the destination | | 1486 | +path to be absolute. When using `'junction'`, the `target` argument will | 1478 | 1487 | automatically be normalized to absolute path. | 1479 | 1488 |
| 1480 | 1489 | ### `fsPromises.truncate(path[, len])` | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/fs/promises.js +12-1Lines changed: 12 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -117,6 +117,8 @@ const { | 117 | 117 | const getDirectoryEntriesPromise = promisify(getDirents); | 118 | 118 | const validateRmOptionsPromise = promisify(validateRmOptions); | 119 | 119 |
| | 120 | +const isWindows = process.platform === 'win32'; | | 121 | + | 120 | 122 | let cpPromises; | 121 | 123 | function lazyLoadCpPromises() { | 122 | 124 | return cpPromises ??= require('internal/fs/cp/cp').cpFn; | @@ -714,7 +716,16 @@ async function readlink(path, options) { | 714 | 716 | } | 715 | 717 |
| 716 | 718 | async function symlink(target, path, type_) { | 717 | | - const type = (typeof type_ === 'string' ? type_ : null); | | 719 | + let type = (typeof type_ === 'string' ? type_ : null); | | 720 | + if (isWindows && type === null) { | | 721 | + try { | | 722 | + const absoluteTarget = pathModule.resolve(`${path}`, '..', `${target}`); | | 723 | + type = (await stat(absoluteTarget)).isDirectory() ? 'dir' : 'file'; | | 724 | + } catch { | | 725 | + // Default to 'file' if path is invalid or file does not exist | | 726 | + type = 'file'; | | 727 | + } | | 728 | + } | 718 | 729 | target = getValidatedPath(target, 'target'); | 719 | 730 | path = getValidatedPath(path); | 720 | 731 | return binding.symlink(preprocessSymlinkDestination(target, type, path), | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-fs-symlink-dir.js +15Lines changed: 15 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -12,6 +12,7 @@ if (!common.canCreateSymLink()) | 12 | 12 | const assert = require('assert'); | 13 | 13 | const path = require('path'); | 14 | 14 | const fs = require('fs'); | | 15 | +const fsPromises = fs.promises; | 15 | 16 |
| 16 | 17 | const tmpdir = require('../common/tmpdir'); | 17 | 18 | tmpdir.refresh(); | @@ -36,11 +37,18 @@ function testAsync(target, path) { | 36 | 37 | })); | 37 | 38 | } | 38 | 39 |
| | 40 | +async function testPromises(target, path) { | | 41 | + await fsPromises.symlink(target, path); | | 42 | + fs.readdirSync(path); | | 43 | +} | | 44 | + | 39 | 45 | for (const linkTarget of linkTargets) { | 40 | 46 | fs.mkdirSync(path.resolve(tmpdir.path, linkTarget)); | 41 | 47 | for (const linkPath of linkPaths) { | 42 | 48 | testSync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-sync`); | 43 | 49 | testAsync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-async`); | | 50 | + testPromises(linkTarget, `${linkPath}-${path.basename(linkTarget)}-promises`) | | 51 | + .then(common.mustCall()); | 44 | 52 | } | 45 | 53 | } | 46 | 54 |
| @@ -57,10 +65,17 @@ for (const linkTarget of linkTargets) { | 57 | 65 | })); | 58 | 66 | } | 59 | 67 |
| | 68 | + async function testPromises(target, path) { | | 69 | + await fsPromises.symlink(target, path); | | 70 | + assert(!fs.existsSync(path)); | | 71 | + } | | 72 | + | 60 | 73 | for (const linkTarget of linkTargets.map((p) => p + '-broken')) { | 61 | 74 | for (const linkPath of linkPaths) { | 62 | 75 | testSync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-sync`); | 63 | 76 | testAsync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-async`); | | 77 | + testPromises(linkTarget, `${linkPath}-${path.basename(linkTarget)}-promises`) | | 78 | + .then(common.mustCall()); | 64 | 79 | } | 65 | 80 | } | 66 | 81 | } |
|