◆ Windows はファイルとディレクトリのパスの最後に / があっても true
◆ Linux はファイルのパスの最後に / があると false

準備

fs.mkdirSync("testdir")
fs.writeFileSync("testfile", "")

try

> fs.existsSync("testdir")
true
> fs.existsSync("testdir/")
true
> fs.existsSync("testfile")
true
> fs.existsSync("testfile/")
false

末尾の / は切り捨ててチェックしてると思ったのですが ファイルの場合に / があると false になりました
ファイルでもディレクトリでもいいからあったら true がほしいときには / を自分で消さないといけないです

resolve すると絶対パスになりますが最後の / は消えるので

fs.existsSync(path.resolve("testfile/"))

とすれば大丈夫です

と ここで終わるつもりだったのですが
ふと Windows だとどうなの?と思って Windows 環境で試してみたら true でした
/ を \\ にしても一緒です
(上の結果は Linux のもの)

試した Node.js のバージョンは Windows Linux ともに 10.5.0 です
Windows の共有フォルダを Linux で cifs マウントしてそのフォルダ上で試しても Linux と同じ結果でした
ファイルシステムというより OS 側の処理の都合 もしくは Node.js 内部のライブラリは Windows と Linux で別の処理なのでそこで違うんだと思います

こういう違いもあるので 最後の / は外したほうがいいですね

追記

もう少し詳しく調べてみました

今の master ブランチと 10.5.0 では実装が変わっていたので 10.5.0 のコードについてです
https://github.com/nodejs/node/blob/v10.5.0/lib/fs.js

fs.existsSync では accessSync が呼び出されていて そこでエラーが発生しなければ true (ファイルやフォルダあり) という扱いです
accessSync の中では path をフォーマットした上で最終的にネイティブコードの binding.access が実行されています

binding.access(pathModule.toNamespacedPath(path), mode, undefined, ctx);

binding は

const binding = process.binding("fs")

で C++ で書かれたネイティブモジュールを呼び出すものです

ただ これを実行する前に実行している pathModule.toNamespacedPath という関数の結果が Windows と Linux で異なります
この関数自体が Windows 用のもので 与えられたパスに対応する 名前空間プレフィック付きのパスを返すものらしいです
https://nodejs.org/api/path.html#path_path_tonamespacedpath_path

Windows ではいろいろな処理をして

\\?\C:\Users\user0\Desktop\file

と \\?\ が頭についた絶対パスになりますが Linux ではそのままになります
この時点で最後の / は消えます
resolve をしてるような感じです

このおかげで Windows は / ありのファイルでも true となりますが Linux は渡したパスそのままなので / ありは見つからないとなります

一応ネイティブモジュールの呼び出し時の最終的な値は⇩のようになっていて ctx には元のパスが入ってますがこっちは影響してないみたいです

binding.access("\\?\C:\Users\user0\Desktop\file", 0, undefined, {path: "file/"});