'node:os' モジュールを使用した標準ディレクトリのパス取得path.normalize():パスの正規化path.resolve()(引数1つ):パスの正規化と完全修飾化path.relative():相対パスの作成path.format():部分からパスを作成するfile: URLの使用URLクラスfile: URLこの章では、Node.jsにおけるファイルシステムパスとファイルURLの使用方法を学びます。
この章では、Node.jsにおけるパス関連の機能を探ります。
'node:path'モジュールにあります。processには、*カレントワーキングディレクトリ*を変更するためのメソッドがあります(その意味についてはすぐに説明します)。'node:os'モジュールには、重要なディレクトリのパスを返す関数が含まれています。'node:path' APIへのアクセス方法3種類'node:path'モジュールは、多くの場合、次のようにインポートされます。
import * as path from 'node:path';この章では、このインポート文は時々省略されます。また、次のインポートも省略します。
import * as assert from 'node:assert/strict';NodeのパスAPIには3つの方法でアクセスできます。
プラットフォーム固有のバージョンのAPIにアクセスできます。
path.posixはmacOSを含むUnix系システムをサポートします。path.win32はWindowsをサポートします。path自体は常に現在のプラットフォームをサポートします。たとえば、これはmacOSでのREPLの対話です。
> path.parse === path.posix.parse
trueファイルシステムパスを解析する関数path.parse()が、2つのプラットフォームでどのように異なるかを見てみましょう。
> path.win32.parse(String.raw`C:\Users\jane\file.txt`)
{
dir: 'C:\\Users\\jane',
root: 'C:\\',
base: 'file.txt',
name: 'file',
ext: '.txt',
}
> path.posix.parse(String.raw`C:\Users\jane\file.txt`)
{
dir: '',
root: '',
base: 'C:\\Users\\jane\\file.txt',
name: 'C:\\Users\\jane\\file',
ext: '.txt',
}Windowsのパスを解析します。最初にpath.win32 APIを使用して正しく解析し、次にpath.posix APIを使用して解析します。後者の場合、パスが正しく分割されていないことがわかります。たとえば、ファイルの基本名はfile.txtである必要があります(他のプロパティの意味については後で詳しく説明します)。
用語
空でないパスは、1つ以上の*パスセグメント*で構成されます。ほとんどの場合、ディレクトリまたはファイルの名前です。
パス内の2つの隣接するパスセグメントを区切るために*パスセパレータ*が使用されます。path.sepには、現在のプラットフォームのパスセパレータが含まれています。
assert.equal(
path.posix.sep, '/' // Path separator on Unix
);
assert.equal(
path.win32.sep, '\\' // Path separator on Windows
);パスリスト内の要素を区切るために*パスデリミタ*が使用されます。path.delimiterには、現在のプラットフォームのパスデリミタが含まれています。
assert.equal(
path.posix.delimiter, ':' // Path delimiter on Unix
);
assert.equal(
path.win32.delimiter, ';' // Path delimiter on Windows
);PATHシェル変数を調べると、パスセパレータとパスデリミタを確認できます。PATHシェル変数には、シェルでコマンドが入力されたときに、オペレーティングシステムが実行可能ファイルを探すパスが含まれています。
これはmacOSのPATH(シェル変数$PATH)の例です。
> process.env.PATH.split(/(?<=:)/)
[
'/opt/homebrew/bin:',
'/opt/homebrew/sbin:',
'/usr/local/bin:',
'/usr/bin:',
'/bin:',
'/usr/sbin:',
'/sbin',
]後方参照アサーション(?<=:)は、指定された位置の前にコロンがある場合に一致しますが、何もキャプチャしません。そのため、パスデリミタ':'は前のパスに含まれています。後方参照アサーションについてはこちら
これはWindowsのPATH(シェル変数%Path%)の例です。
> process.env.Path.split(/(?<=;)/)
[
'C:\\Windows\\system32;',
'C:\\Windows;',
'C:\\Windows\\System32\\Wbem;',
'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;',
'C:\\Windows\\System32\\OpenSSH\\;',
'C:\\ProgramData\\chocolatey\\bin;',
'C:\\Program Files\\nodejs\\',
]多くのシェルには、*カレントワーキングディレクトリ*(CWD)という概念があります。「現在いるディレクトリ」です。
cdです。processはグローバルなNode.js変数です。CWDを取得および設定するためのメソッドを提供します。
process.cwd()はCWDを返します。process.chdir(dirPath)はCWDをdirPathに変更します。dirPathにはディレクトリが存在する必要があります。Node.jsは、パスが*完全修飾パス*(完全なパス)でない場合、CWDを使用して不足している部分を補います。これにより、様々な関数(例:fs.readFileSync())で部分修飾パスを使用できます。
次のコードは、Unixでのprocess.chdir()とprocess.cwd()を示しています。
process.chdir('/home/jane');
assert.equal(
process.cwd(), '/home/jane'
);これまで、Unixでのカレントワーキングディレクトリを使用してきました。Windowsは異なります。
path.chdir()を使用して、同時に両方設定できます。
process.chdir('C:\\Windows');
process.chdir('Z:\\tmp');ドライブを再び参照すると、Node.jsはそのドライブの以前のカレントディレクトリを記憶しています。
assert.equal(
process.cwd(), 'Z:\\tmp'
);
process.chdir('C:');
assert.equal(
process.cwd(), 'C:\\Windows'
);Unixは2種類のパスのみ認識します。
*絶対パス*は完全修飾パスであり、スラッシュで始まります。
/home/john/proj*相対パス*は部分修飾パスであり、ファイル名またはドットで始まります。
. (current directory)
.. (parent directory)
dir
./dir
../dir
../../dir/subdirpath.resolve()(後述)を使用して、相対パスを絶対パスに対して解決してみましょう。結果は絶対パスになります。
> const abs = '/home/john/proj';
> path.resolve(abs, '.')
'/home/john/proj'
> path.resolve(abs, '..')
'/home/john'
> path.resolve(abs, 'dir')
'/home/john/proj/dir'
> path.resolve(abs, './dir')
'/home/john/proj/dir'
> path.resolve(abs, '../dir')
'/home/john/dir'
> path.resolve(abs, '../../dir/subdir')
'/home/dir/subdir'Windowsは4種類のパスを区別します(詳細については、Microsoftのドキュメントを参照してください)。
ドライブ文字を含む絶対パスは完全修飾パスです。その他のパスはすべて部分修飾パスです。
ドライブ文字のない**絶対パスの解決**は、完全修飾パスfullに対して行われ、fullのドライブ文字が取得されます。
> const full = 'C:\\Users\\jane\\proj';
> path.resolve(full, '\\Windows')
'C:\\Windows'ドライブ文字のない**相対パスの解決**は、完全修飾パスに対して行われ、後者を更新したものと見なすことができます。
> const full = 'C:\\Users\\jane\\proj';
> path.resolve(full, '.')
'C:\\Users\\jane\\proj'
> path.resolve(full, '..')
'C:\\Users\\jane'
> path.resolve(full, 'dir')
'C:\\Users\\jane\\proj\\dir'
> path.resolve(full, '.\\dir')
'C:\\Users\\jane\\proj\\dir'
> path.resolve(full, '..\\dir')
'C:\\Users\\jane\\dir'
> path.resolve(full, '..\\..\\dir')
'C:\\Users\\dir'ドライブ文字を含む**相対パスrelの解決**は、完全修飾パスfullに対して行われ、relのドライブ文字に依存します。
fullと同じドライブ文字?fullに対してrelを解決します。fullとは異なるドライブ文字?relのドライブのカレントディレクトリに対してrelを解決します。次のようになります。
// Configure current directories for C: and Z:
process.chdir('C:\\Windows\\System');
process.chdir('Z:\\tmp');
const full = 'C:\\Users\\jane\\proj';
// Same drive letter
assert.equal(
path.resolve(full, 'C:dir'),
'C:\\Users\\jane\\proj\\dir'
);
assert.equal(
path.resolve(full, 'C:'),
'C:\\Users\\jane\\proj'
);
// Different drive letter
assert.equal(
path.resolve(full, 'Z:dir'),
'Z:\\tmp\\dir'
);
assert.equal(
path.resolve(full, 'Z:'),
'Z:\\tmp'
);'node:os'モジュールを使用した標準ディレクトリのパス取得'node:os'モジュールは、2つの重要なディレクトリのパスを提供します。
os.homedir()は、現在のユーザーのホームディレクトリのパスを返します。例:
> os.homedir() // macOS
'/Users/rauschma'
> os.homedir() // Windows
'C:\\Users\\axel'os.tmpdir()は、オペレーティングシステムの一時ファイル用ディレクトリのパスを返します。例:
> os.tmpdir() // macOS
'/var/folders/ph/sz0384m11vxf5byk12fzjms40000gn/T'
> os.tmpdir() // Windows
'C:\\Users\\axel\\AppData\\Local\\Temp'パスを連結するための関数は2つあります。
path.resolve()は常に完全修飾パスを返します。path.join()は相対パスを保持します。path.resolve():完全修飾パスを作成するためのパスの連結path.resolve(...paths: Array<string>): stringpathsを連結して完全修飾パスを返します。次のアルゴリズムを使用します。
path[0]を解決します。path[1]を解決します。引数なしの場合、path.resolve()はカレントワーキングディレクトリのパスを返します。
> process.cwd()
'/usr/local'
> path.resolve()
'/usr/local'1つ以上の相対パスが、カレントワーキングディレクトリから始めて解決に使用されます。
> path.resolve('.')
'/usr/local'
> path.resolve('..')
'/usr'
> path.resolve('bin')
'/usr/local/bin'
> path.resolve('./bin', 'sub')
'/usr/local/bin/sub'
> path.resolve('../lib', 'log')
'/usr/lib/log'完全修飾パスは、前の結果を置き換えます。
> path.resolve('bin', '/home')
'/home'これにより、部分修飾パスを完全修飾パスに対して解決できます。
> path.resolve('/home/john', 'proj', 'src')
'/home/john/proj/src'path.join():相対パスを保持したままパスの連結を行うpath.join(...paths: Array<string>): stringpaths[0]から開始し、残りのパスを昇順または降順の指示として解釈します。path.resolve()とは異なり、この関数は部分修飾パスを保持します。paths[0]が部分修飾パスであれば、結果は部分修飾パスになります。完全修飾パスであれば、結果は完全修飾パスになります。
降順の例
> path.posix.join('/usr/local', 'sub', 'subsub')
'/usr/local/sub/subsub'
> path.posix.join('relative/dir', 'sub', 'subsub')
'relative/dir/sub/subsub'ダブルドットは昇順になります。
> path.posix.join('/usr/local', '..')
'/usr'
> path.posix.join('relative/dir', '..')
'relative'単一のドットは何も行いません。
> path.posix.join('/usr/local', '.')
'/usr/local'
> path.posix.join('relative/dir', '.')
'relative/dir'最初の引数以降の引数が完全修飾パスである場合、相対パスとして解釈されます。
> path.posix.join('dir', '/tmp')
'dir/tmp'
> path.win32.join('dir', 'C:\\Users')
'dir\\C:\\Users'3つ以上の引数を使用する場合
> path.posix.join('/usr/local', '../lib', '.', 'log')
'/usr/lib/log'path.normalize():パスの正規化を確保するpath.normalize(path: string): stringUnixでは、path.normalize()は
.)であるパスセグメントを削除します。..)であるパスセグメントを解決します。例:
// Fully qualified path
assert.equal(
path.posix.normalize('/home/./john/lib/../photos///pet'),
'/home/john/photos/pet'
);
// Partially qualified path
assert.equal(
path.posix.normalize('./john/lib/../photos///pet'),
'john/photos/pet'
);Windowsでは、path.normalize()は
.)であるパスセグメントを削除します。..)であるパスセグメントを解決します。/)(有効です)を推奨されるパス区切り文字(\)に変換します。例:
// Fully qualified path
assert.equal(
path.win32.normalize('C:\\Users/jane\\doc\\..\\proj\\\\src'),
'C:\\Users\\jane\\proj\\src'
);
// Partially qualified path
assert.equal(
path.win32.normalize('.\\jane\\doc\\..\\proj\\\\src'),
'jane\\proj\\src'
);path.join()に単一の引数を指定した場合も正規化され、path.normalize()と同じように動作することに注意してください。
> path.posix.normalize('/home/./john/lib/../photos///pet')
'/home/john/photos/pet'
> path.posix.join('/home/./john/lib/../photos///pet')
'/home/john/photos/pet'
> path.posix.normalize('./john/lib/../photos///pet')
'john/photos/pet'
> path.posix.join('./john/lib/../photos///pet')
'john/photos/pet'path.resolve()(1つの引数):パスの正規化と完全修飾を確保する既にpath.resolve()について説明しました。単一の引数で呼び出されると、パスを正規化し、完全修飾されていることを確認します。
Unixでpath.resolve()を使用する
> process.cwd()
'/usr/local'
> path.resolve('/home/./john/lib/../photos///pet')
'/home/john/photos/pet'
> path.resolve('./john/lib/../photos///pet')
'/usr/local/john/photos/pet'Windowsでpath.resolve()を使用する
> process.cwd()
'C:\\Windows\\System'
> path.resolve('C:\\Users/jane\\doc\\..\\proj\\\\src')
'C:\\Users\\jane\\proj\\src'
> path.resolve('.\\jane\\doc\\..\\proj\\\\src')
'C:\\Windows\\System\\jane\\proj\\src'path.relative():相対パスの作成path.relative(sourcePath: string, destinationPath: string): stringsourcePathからdestinationPathへの相対パスを返します。
> path.posix.relative('/home/john/', '/home/john/proj/my-lib/README.md')
'proj/my-lib/README.md'
> path.posix.relative('/tmp/proj/my-lib/', '/tmp/doc/zsh.txt')
'../../doc/zsh.txt'Windowsでは、sourcePathとdestinationPathが異なるドライブにある場合、完全修飾パスが取得されます。
> path.win32.relative('Z:\\tmp\\', 'C:\\Users\\Jane\\')
'C:\\Users\\Jane'この関数は相対パスでも機能します。
> path.posix.relative('proj/my-lib/', 'doc/zsh.txt')
'../../doc/zsh.txt'path.parse():パス部分をオブジェクトで作成するtype PathObject = {
dir: string,
root: string,
base: string,
name: string,
ext: string,
};
path.parse(path: string): PathObjectpathの様々な部分を抽出し、以下のプロパティを持つオブジェクトで返します。
.base:パスの最後のセグメント.ext:baseのファイル名拡張子.name:拡張子を除いたbase。この部分はパスの *ステム* とも呼ばれます。.root:パスの開始部分(最初のセグメントの前).dir:baseが存在するディレクトリ - baseを除いたパス後で、path.parse()の逆関数であるpath.format()関数について説明します。これは、パス部分を持つオブジェクトをパスに変換します。
path.parse()Unixでpath.parse()を使用すると、次のようになります。
> path.posix.parse('/home/jane/file.txt')
{
dir: '/home/jane',
root: '/',
base: 'file.txt',
name: 'file',
ext: '.txt',
}次の図は、各部分の範囲を視覚的に表したものです。
/ home/jane / file .txt
| root | | name | ext |
| dir | base |
例えば、.dirはbaseを除いたパスであり、.baseは.nameと.extの組み合わせであることがわかります。
path.parse()Windowsでのpath.parse()の動作は次のとおりです。
> path.win32.parse(String.raw`C:\Users\john\file.txt`)
{
dir: 'C:\\Users\\john',
root: 'C:\\',
base: 'file.txt',
name: 'file',
ext: '.txt',
}これは結果の図です。
C:\ Users\john \ file .txt
| root | | name | ext |
| dir | base |
path.basename():パスのbaseを抽出するpath.basename(path, ext?)pathのbaseを返します。
> path.basename('/home/jane/file.txt')
'file.txt'オプションで、この関数はサフィックスも削除できます。
> path.basename('/home/jane/file.txt', '.txt')
'file'
> path.basename('/home/jane/file.txt', 'txt')
'file.'
> path.basename('/home/jane/file.txt', 'xt')
'file.t'拡張子の削除は大文字と小文字が区別されます - Windowsでも!
> path.win32.basename(String.raw`C:\Users\john\file.txt`, '.txt')
'file'
> path.win32.basename(String.raw`C:\Users\john\file.txt`, '.TXT')
'file.txt'path.dirname():パスの親ディレクトリを抽出するpath.dirname(path)pathにあるファイルまたはディレクトリの親ディレクトリを返します。
> path.win32.dirname(String.raw`C:\Users\john\file.txt`)
'C:\\Users\\john'
> path.win32.dirname('C:\\Users\\john\\dir\\')
'C:\\Users\\john'
> path.posix.dirname('/home/jane/file.txt')
'/home/jane'
> path.posix.dirname('/home/jane/dir/')
'/home/jane'path.extname():パスの拡張子を抽出するpath.extname(path)pathの拡張子を返します。
> path.extname('/home/jane/file.txt')
'.txt'
> path.extname('/home/jane/file.')
'.'
> path.extname('/home/jane/file')
''
> path.extname('/home/jane/')
''
> path.extname('/home/jane')
''path.isAbsolute():指定されたパスは絶対パスか?path.isAbsolute(path: string): booleanpathが絶対パスの場合はtrue、それ以外の場合はfalseを返します。
Unixでの結果は簡単です。
> path.posix.isAbsolute('/home/john')
true
> path.posix.isAbsolute('john')
falseWindowsでは、「絶対」は必ずしも「完全修飾」を意味しません(最初のパスのみが完全修飾されます)。
> path.win32.isAbsolute('C:\\Users\\jane')
true
> path.win32.isAbsolute('\\Users\\jane')
true
> path.win32.isAbsolute('C:jane')
false
> path.win32.isAbsolute('jane')
falsepath.format():部分からパスを作成するtype PathObject = {
dir: string,
root: string,
base: string,
name: string,
ext: string,
};
path.format(pathObject: PathObject): stringパスオブジェクトからパスを作成します。
> path.format({dir: '/home/jane', base: 'file.txt'})
'/home/jane/file.txt'path.format()を使用して、パスの拡張子を変更できます。
function changeFilenameExtension(pathStr, newExtension) {
if (!newExtension.startsWith('.')) {
throw new Error(
'Extension must start with a dot: '
+ JSON.stringify(newExtension)
);
}
const parts = path.parse(pathStr);
return path.format({
...parts,
base: undefined, // prevent .base from overriding .name and .ext
ext: newExtension,
});
}
assert.equal(
changeFilenameExtension('/tmp/file.md', '.html'),
'/tmp/file.html'
);
assert.equal(
changeFilenameExtension('/tmp/file', '.html'),
'/tmp/file.html'
);
assert.equal(
changeFilenameExtension('/tmp/file/', '.html'),
'/tmp/file.html'
);元のファイル名拡張子がわかっている場合は、正規表現を使用してファイル名拡張子を変更することもできます。
> '/tmp/file.md'.replace(/\.md$/i, '.html')
'/tmp/file.html'
> '/tmp/file.MD'.replace(/\.md$/i, '.html')
'/tmp/file.html'異なるプラットフォームで同じパスを使用したい場合があります。その場合、2つの問題があります。
例として、データを含むディレクトリで動作するNode.jsアプリを考えてみましょう。アプリは2種類のパスで構成できると仮定します。
前述の問題のため
プラットフォーム間で完全修飾パスを再利用できません。
データディレクトリを指すパスを再利用できます。このようなパスは、構成ファイル(データディレクトリ内にある場合とない場合があります)やアプリのコード内の定数に保存できます。そのためには
次のセクションでは、両方をどのように実現できるかを説明します。
プラットフォームに依存しない相対パスは、パスセグメントの配列として保存し、次のようにプラットフォーム固有の完全修飾パスに変換できます。
const universalRelativePath = ['static', 'img', 'logo.jpg'];
const dataDirUnix = '/home/john/data-dir';
assert.equal(
path.posix.resolve(dataDirUnix, ...universalRelativePath),
'/home/john/data-dir/static/img/logo.jpg'
);
const dataDirWindows = 'C:\\Users\\jane\\data-dir';
assert.equal(
path.win32.resolve(dataDirWindows, ...universalRelativePath),
'C:\\Users\\jane\\data-dir\\static\\img\\logo.jpg'
);プラットフォーム固有の相対パスを作成するには、以下を使用できます。
const dataDir = '/home/john/data-dir';
const pathInDataDir = '/home/john/data-dir/static/img/logo.jpg';
assert.equal(
path.relative(dataDir, pathInDataDir),
'static/img/logo.jpg'
);次の関数は、プラットフォーム固有の相対パスをプラットフォームに依存しないパスに変換します。
import * as path from 'node:path';
function splitRelativePathIntoSegments(relPath) {
if (path.isAbsolute(relPath)) {
throw new Error('Path isn’t relative: ' + relPath);
}
relPath = path.normalize(relPath);
const result = [];
while (true) {
const base = path.basename(relPath);
if (base.length === 0) break;
result.unshift(base);
const dir = path.dirname(relPath);
if (dir === '.') break;
relPath = dir;
}
return result;
}UnixでsplitRelativePathIntoSegments()を使用する
> splitRelativePathIntoSegments('static/img/logo.jpg')
[ 'static', 'img', 'logo.jpg' ]
> splitRelativePathIntoSegments('file.txt')
[ 'file.txt' ]WindowsでsplitRelativePathIntoSegments()を使用する
> splitRelativePathIntoSegments('static/img/logo.jpg')
[ 'static', 'img', 'logo.jpg' ]
> splitRelativePathIntoSegments('C:static/img/logo.jpg')
[ 'static', 'img', 'logo.jpg' ]
> splitRelativePathIntoSegments('file.txt')
[ 'file.txt' ]
> splitRelativePathIntoSegments('C:file.txt')
[ 'file.txt' ]npmモジュール'minimatch'を使用すると、*glob式*、*globパターン*、または*glob*と呼ばれるパターンに対してパスを照合できます。
import minimatch from 'minimatch';
assert.equal(
minimatch('/dir/sub/file.txt', '/dir/sub/*.txt'), true
);
assert.equal(
minimatch('/dir/sub/file.txt', '/**/file.txt'), true
);globの使用例
その他のglobライブラリ
minimatchのAPI全体は、プロジェクトのREADMEファイルに記載されています。このセクションでは、最も重要な機能を見ていきます。
MinimatchはglobをJavaScriptのRegExpオブジェクトにコンパイルし、それを使用して照合します。
minimatch():一度コンパイルして照合するminimatch(path: string, glob: string, options?: MinimatchOptions): booleanglobがpathと一致する場合はtrue、それ以外の場合はfalseを返します。
2つの興味深いオプション
.dot: boolean(デフォルト:false)trueの場合、*や**などのワイルドカード記号は、「非表示」のパスセグメント(名前がドットで始まるセグメント)と一致します。
> minimatch('/usr/local/.tmp/data.json', '/usr/**/data.json')
false
> minimatch('/usr/local/.tmp/data.json', '/usr/**/data.json', {dot: true})
true
> minimatch('/tmp/.log/events.txt', '/tmp/*/events.txt')
false
> minimatch('/tmp/.log/events.txt', '/tmp/*/events.txt', {dot: true})
true.matchBase: boolean(デフォルト:false)trueの場合、スラッシュのないパターンはパスのbasenameと一致します。
> minimatch('/dir/file.txt', 'file.txt')
false
> minimatch('/dir/file.txt', 'file.txt', {matchBase: true})
truenew minimatch.Minimatch():一度コンパイルして複数回照合するminimatch.Minimatchクラスを使用すると、globを正規表現に一度だけコンパイルして、複数回照合できます。
new Minimatch(pattern: string, options?: MinimatchOptions)このクラスは次のように使用します。
import minimatch from 'minimatch';
const {Minimatch} = minimatch;
const glob = new Minimatch('/dir/sub/*.txt');
assert.equal(
glob.match('/dir/sub/file.txt'), true
);
assert.equal(
glob.match('/dir/sub/notes.txt'), true
);このセクションでは、構文の基本事項について説明します。しかし、さらに多くの機能があります。これらはここに記載されています。
Windowsでも、globセグメントはスラッシュで区切られますが、バックスラッシュとスラッシュの両方と一致します(Windowsでは有効なパス区切り文字です)。
> minimatch('dir\\sub/file.txt', 'dir/sub/file.txt')
trueMinimatchはパスを自動的に正規化しません。
> minimatch('./file.txt', './file.txt')
true
> minimatch('./file.txt', 'file.txt')
false
> minimatch('file.txt', './file.txt')
falseしたがって、自分で作成しない場合は、パスを正規化する必要があります。
> path.normalize('./file.txt')
'file.txt'ワイルドカード記号(より柔軟に一致する)のないパターンは、正確に一致する必要があります。特に、パス区切り文字は一致する必要があります。
> minimatch('/dir/file.txt', '/dir/file.txt')
true
> minimatch('dir/file.txt', 'dir/file.txt')
true
> minimatch('/dir/file.txt', 'dir/file.txt')
false
> minimatch('/dir/file.txt', 'file.txt')
falseつまり、絶対パスか相対パスのどちらかを選択する必要があります。
.matchBaseオプションを使用すると、スラッシュのないパターンをパスのbasenameと照合できます。
> minimatch('/dir/file.txt', 'file.txt', {matchBase: true})
true*)は、単一のセグメントの任意の部分と一致しますワイルドカード記号であるアスタリスク(*)は、任意のパスセグメントまたはセグメントの任意の部分と一致します。
> minimatch('/dir/file.txt', '/*/file.txt')
true
> minimatch('/tmp/file.txt', '/*/file.txt')
true
> minimatch('/dir/file.txt', '/dir/*.txt')
true
> minimatch('/dir/data.txt', '/dir/*.txt')
trueアスタリスクは、名前がドットで始まる「非表示ファイル」とは一致しません。それらを一致させたい場合は、アスタリスクの前にドットを付ける必要があります。
> minimatch('file.txt', '*')
true
> minimatch('.gitignore', '*')
false
> minimatch('.gitignore', '.*')
true
> minimatch('/tmp/.log/events.txt', '/tmp/*/events.txt')
false.dotオプションを使用して、この動作をオフにできます。
> minimatch('.gitignore', '*', {dot: true})
true
> minimatch('/tmp/.log/events.txt', '/tmp/*/events.txt', {dot: true})
true**)は、0個以上のセグメントと一致します**/は、0個以上のセグメントと一致します。
> minimatch('/file.txt', '/**/file.txt')
true
> minimatch('/dir/file.txt', '/**/file.txt')
true
> minimatch('/dir/sub/file.txt', '/**/file.txt')
true相対パスと一致させたい場合でも、パターンはパス区切り文字で始まらないようにする必要があります。
> minimatch('file.txt', '/**/file.txt')
falseダブルアスタリスクは、名前がドットで始まる「非表示」のパスセグメントとは一致しません。
> minimatch('/usr/local/.tmp/data.json', '/usr/**/data.json')
false.dotオプションを使用して、この動作をオフにできます。
> minimatch('/usr/local/.tmp/data.json', '/usr/**/data.json', {dot: true})
trueglobを感嘆符で始めると、感嘆符の後のパターンと一致しない場合に一致します。
> minimatch('file.txt', '!**/*.txt')
false
> minimatch('file.js', '!**/*.txt')
true中括弧内のコンマ区切りのパターンは、いずれかのパターンと一致する場合に一致します。
> minimatch('file.txt', 'file.{txt,js}')
true
> minimatch('file.js', 'file.{txt,js}')
trueダブルドットで区切られた整数のペアは、整数の範囲を定義し、その要素のいずれかと一致する場合に一致します。
> minimatch('file1.txt', 'file{1..3}.txt')
true
> minimatch('file2.txt', 'file{1..3}.txt')
true
> minimatch('file3.txt', 'file{1..3}.txt')
true
> minimatch('file4.txt', 'file{1..3}.txt')
falseゼロパディングもサポートされています。
> minimatch('file1.txt', 'file{01..12}.txt')
false
> minimatch('file01.txt', 'file{01..12}.txt')
true
> minimatch('file02.txt', 'file{01..12}.txt')
true
> minimatch('file12.txt', 'file{01..15}.txt')
truefile: URLの使用Node.jsでファイルを参照する一般的な方法は2つあります。
file:であるURLのインスタンス例:
assert.equal(
fs.readFileSync(
'/tmp/data.txt', {encoding: 'utf-8'}),
'Content'
);
assert.equal(
fs.readFileSync(
new URL('file:///tmp/data.txt'), {encoding: 'utf-8'}),
'Content'
);URLクラスこのセクションでは、URLクラスを詳しく見ていきます。このクラスの詳細については、
この章では、他のWebプラットフォームで使用されている方法と同じように、グローバル変数経由でURLクラスにアクセスします。しかし、インポートすることもできます。
import {URL} from 'node:url';URLはURIのサブセットです。URIの標準であるRFC 3986では、2種類のURI参照が区別されています。
URLのコンストラクタURLクラスは2つの方法でインスタンス化できます。
new URL(uri: string)
uriはURIである必要があります。新しいインスタンスのURIを指定します。
new URL(uriRef: string, baseUri: string)
baseUriはURIである必要があります。uriRefが相対参照の場合、baseUriに対して解決され、その結果が新しいインスタンスのURIになります。
uriRefがURIの場合、インスタンスの基礎となるデータとしてbaseUriを完全に置き換えます。
ここでは、クラスの動作を確認できます。
// If there is only one argument, it must be a proper URI
assert.equal(
new URL('https://example.com/public/page.html').toString(),
'https://example.com/public/page.html'
);
assert.throws(
() => new URL('../book/toc.html'),
/^TypeError \[ERR_INVALID_URL\]: Invalid URL$/
);
// Resolve a relative reference against a base URI
assert.equal(
new URL(
'../book/toc.html',
'https://example.com/public/page.html'
).toString(),
'https://example.com/book/toc.html'
);URLのインスタンスに対する相対参照の解決URLコンストラクタのこのバリアントを再検討しましょう。
new URL(uriRef: string, baseUri: string)引数baseUriは文字列に変換されます。したがって、文字列に変換したときに有効なURLになる限り、任意のオブジェクトを使用できます。
const obj = { toString() {return 'https://example.com'} };
assert.equal(
new URL('index.html', obj).href,
'https://example.com/index.html'
);これにより、URLインスタンスに対して相対参照を解決できます。
const url = new URL('https://example.com/dir/file1.html');
assert.equal(
new URL('../file2.html', url).href,
'https://example.com/file2.html'
);このように使用すると、コンストラクタはpath.resolve()と大まかに似ています。
URLインスタンスのプロパティURLのインスタンスには、以下のプロパティがあります。
type URL = {
protocol: string,
username: string,
password: string,
hostname: string,
port: string,
host: string,
readonly origin: string,
pathname: string,
search: string,
readonly searchParams: URLSearchParams,
hash: string,
href: string,
toString(): string,
toJSON(): string,
}URLを文字列に変換する一般的な方法は3つあります。
const url = new URL('https://example.com/about.html');
assert.equal(
url.toString(),
'https://example.com/about.html'
);
assert.equal(
url.href,
'https://example.com/about.html'
);
assert.equal(
url.toJSON(),
'https://example.com/about.html'
);.toJSON()メソッドを使用すると、JSONデータでURLを使用できます。
const jsonStr = JSON.stringify({
pageUrl: new URL('https://exploringjs.dokyumento.jp')
});
assert.equal(
jsonStr, '{"pageUrl":"https://exploringjs.dokyumento.jp"}'
);URLプロパティを取得するURLインスタンスのプロパティは、独自のデータプロパティではなく、ゲッターとセッターによって実装されています。次の例では、ユーティリティ関数pickProps()(コードは最後に示されています)を使用して、これらのゲッターによって返される値をプレーンオブジェクトにコピーします。
const props = pickProps(
new URL('https://jane:pw@example.com:80/news.html?date=today#misc'),
'protocol', 'username', 'password', 'hostname', 'port', 'host',
'origin', 'pathname', 'search', 'hash', 'href'
);
assert.deepEqual(
props,
{
protocol: 'https:',
username: 'jane',
password: 'pw',
hostname: 'example.com',
port: '80',
host: 'example.com:80',
origin: 'https://example.com:80',
pathname: '/news.html',
search: '?date=today',
hash: '#misc',
href: 'https://jane:pw@example.com:80/news.html?date=today#misc'
}
);
function pickProps(input, ...keys) {
const output = {};
for (const key of keys) {
output[key] = input[key];
}
return output;
}残念ながら、パス名は単一の原子単位です。つまり、URLクラスを使用してその部分(ベース、拡張子など)にアクセスすることはできません。
.hostnameなどのプロパティを設定することで、URLの一部を変更することもできます。
const url = new URL('https://example.com');
url.hostname = '2ality.com';
assert.equal(
url.href, 'https://2ality.com/'
);セッターを使用して、部分的なURLを作成できます(Haroen Viaeneによるアイデア)。
// Object.assign() invokes setters when transferring property values
const urlFromParts = (parts) => Object.assign(
new URL('https://example.com'), // minimal dummy URL
parts // assigned to the dummy
);
const url = urlFromParts({
protocol: 'https:',
hostname: '2ality.com',
pathname: '/p/about.html',
});
assert.equal(
url.href, 'https://2ality.com/p/about.html'
);.searchParamsによる検索パラメータの管理.searchParamsプロパティを使用して、URLの検索パラメータを管理できます。その値は、URLSearchParamsのインスタンスです。
これを使用して、検索パラメータを読み取ることができます。
const url = new URL('https://example.com/?topic=js');
assert.equal(
url.searchParams.get('topic'), 'js'
);
assert.equal(
url.searchParams.has('topic'), true
);これを使用して、検索パラメータを変更することもできます。
url.searchParams.append('page', '5');
assert.equal(
url.href, 'https://example.com/?topic=js&page=5'
);
url.searchParams.set('topic', 'css');
assert.equal(
url.href, 'https://example.com/?topic=css&page=5'
);ファイルパスとURLを手動で変換しようとすることがありますが、例えば、URLインスタンスmyUrlをmyUrl.pathnameを介してファイルパスに変換しようとすることができます。しかし、これは必ずしも機能するわけではありません。 この関数を使用する方が良いでしょう。
url.fileURLToPath(url: URL | string): string次のコードは、その関数の結果と.pathnameの値を比較しています。
import * as url from 'node:url';
//::::: Unix :::::
const url1 = new URL('file:///tmp/with%20space.txt');
assert.equal(
url1.pathname, '/tmp/with%20space.txt');
assert.equal(
url.fileURLToPath(url1), '/tmp/with space.txt');
const url2 = new URL('file:///home/thor/Mj%C3%B6lnir.txt');
assert.equal(
url2.pathname, '/home/thor/Mj%C3%B6lnir.txt');
assert.equal(
url.fileURLToPath(url2), '/home/thor/Mjölnir.txt');
//::::: Windows :::::
const url3 = new URL('file:///C:/dir/');
assert.equal(
url3.pathname, '/C:/dir/');
assert.equal(
url.fileURLToPath(url3), 'C:\\dir\\');この関数はurl.fileURLToPath()の逆関数です。
url.pathToFileURL(path: string): URLこれはpathをファイルURLに変換します。
> url.pathToFileURL('/home/john/Work Files').href
'file:///home/john/Work%20Files'URLの重要なユースケースの1つは、現在のモジュールの兄弟であるファイルにアクセスすることです。
function readData() {
const url = new URL('data.txt', import.meta.url);
return fs.readFileSync(url, {encoding: 'UTF-8'});
}この関数は、現在のモジュールのURL(通常はNode.jsではfile: URL)を含むimport.meta.urlを使用します。
fetch()を使用すると、前のコードはさらにクロスプラットフォームになります。しかし、Node.js 18.9.0時点では、fetch()はfile: URLではまだ動作しません。
> await fetch('file:///tmp/file.txt')
TypeError: fetch failed
cause: Error: not implemented... yet...ESMモジュールは2つの方法で使用できます。
モジュールを両方の方法で使用する場合、現在のモジュールがメインモジュールかどうかを確認する必要があります。スクリプト機能を実行するのは、その場合のみだからです。この章では、そのチェックを実行する方法を学習します。
CommonJSでは、次のパターンを使用して、現在のモジュールがエントリポイントであったかどうかを検出できます(ソース:Node.jsドキュメント)。
if (require.main === module) {
// Main CommonJS module
}現時点では、ESMモジュールには、モジュールがメインかどうかを確認する簡単な組み込み方法がありません。代わりに、次の回避策を使用する必要があります(Rich Harrisによるツイートに基づく)。
import * as url from 'node:url';
if (import.meta.url.startsWith('file:')) { // (A)
const modulePath = url.fileURLToPath(import.meta.url);
if (process.argv[1] === modulePath) { // (B)
// Main ESM module
}
}説明
import.meta.urlには、現在実行されているESMモジュールのURLが含まれています。
コードが常にローカルで実行されると確信している場合(将来的にはそれほど一般的ではなくなる可能性があります)、A行のチェックを省略できます。省略してコードがローカルで実行されない場合でも、少なくとも例外が発生します(サイレントエラーにはなりません) - url.fileURLToPath()のおかげです(次の項目を参照)。
url.fileURLToPath()を使用して、URLをローカルパスに変換します。この関数は、プロトコルがfile:でない場合、例外をスローします。
process.argv[1]には、最初のモジュールのパスが含まれています。B行の比較は、この値が常に絶対パスであるため機能します。Node.jsは次のように設定します(ソースコード)。
process.argv[1] = path.resolve(process.argv[1]);file: URLシェルスクリプトがファイルへの参照を受け取ったり、ファイルへの参照をエクスポートしたりする場合(例:画面にログ出力する場合)、それらは事実上常にパスです。ただし、前のセクションで説明したように、URLが必要になるケースが2つあります。