집합교육 1차 완성

main
thkim 2024-10-28 13:57:53 +09:00
parent a07ec5e829
commit a158d08c51
126 changed files with 39945 additions and 16417 deletions

View File

@ -186,12 +186,12 @@ public class MainController {
EgovMap getMemberInfo = mainService.getMemberInfo(params);
if( getMemberInfo != null ) {
if( getMemberInfo != null && getMemberInfo.get("cls") != null) {
System.out.println("mainService.getMemberInfo - 후:" + getMemberInfo.get("cls").toString());
System.out.println("mainService.getMemberInfo - 후:" + getMemberInfo.get("cls").toString() + "- IP:" + ipAdd);
} else {
System.out.println("mainService.getMemberInfo - 후: getMemberInfo.get(\"cls\") is null");
System.out.println("mainService.getMemberInfo - 후: getMemberInfo.get(\"cls\") is null - IP:" + ipAdd);
}
} else {
System.out.println("mainService.getMemberInfo - 후: null");
System.out.println("mainService.getMemberInfo - 후: null - IP:" + ipAdd);
}
if (getMemberInfo != null) {

View File

@ -672,8 +672,7 @@ public class GeneralUserMngController {
"\n--------------------------------------------------------------\n" +
"strJSON" + strJSON + "\n" +
"\n--------------------------------------------------------------\n"
);
);
JSONParser jsonParser = new JSONParser();
JSONArray jsonArr = null;
boolean isFail = false;
@ -697,7 +696,6 @@ public class GeneralUserMngController {
long lWvtRegId = MyUtil.getLongFromObject(hashMap.get("p_wvt_reg_id"));
if( nRetCode == 100 ) {
jsonResponse.put("resultCode", nRetCode);
jsonResponse.put("result", "true");

View File

@ -32,16 +32,16 @@ public class HomeTrainingServiceImpl implements HomeTrainingService {
@Override
public HashMap<String, Object> addHomeTraining(HttpServletRequest request, HttpServletResponse response, HashMap<String, Object> params) throws Exception {
if (!UserInfo.isValidSession(request, response, "admin")) {
throw new Exception("로그인이 필요한 서비스입니다.");
}
System.out.println( "thkim test 5");
params.put("trainingAdmin", "류지송");
params.put("trainingTel", "031-995-0934");
String userId = (String)request.getSession().getAttribute("admin.userID");
params.put("userId", userId);
params.put("userId", userId);
String str = MyUtil.getStringFromObject(params.get("trainingDatetime") );
// DateTimeFormatter를 사용하여 문자열을 LocalDateTime으로 파싱합니다.
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

View File

@ -157,6 +157,15 @@
const wvtRegId = e.getAttribute('data-seq');
const trainingName = e.getAttribute('data-training-name');
// 클릭된 행에서 input radio 요소 찾기
const radioInput = e.querySelector('input[type="radio"][name="wht-reg-id"]');
// input radio 요소 체크
if (radioInput) {
radioInput.checked = true;
}
xhr.open('GET', 'home-training-index/item/list.do?wvtRegId=' + wvtRegId, true);
xhr.setRequestHeader('Content-type', 'application/json');
@ -231,9 +240,9 @@
} else if( stateCode === 'C' ) {
return '취소됨';
} else if( stateCode === 'P' ) {
return '예정';
return '오픈전';
} else if( stateCode === 'F' ) {
return '마감';
return '마감';
}
}
@ -249,8 +258,18 @@
const homeVisitListEle = document.getElementById('home-visit-list');
let homeVisitListHTML = '';
let aliveEducationsCount = 0;
for( idx in data ) {
////A:접수중,D:삭제,C:취소,P:예정,F:마감
if( data[idx].stateCode === 'D' ) {
continue;
} else if( data[idx].stateCode === 'C' ) {
continue;
}
aliveEducationsCount++;
homeVisitListHTML +=
`
<tr onClick="onClickCourseItem(this)" data-seq="` + data[idx].whtRegId +`" data-training-name="` + data[idx].trainingName +`" >
@ -264,7 +283,7 @@
</tr>
`;
}
if( data.length === 0 ) {
if( aliveEducationsCount === 0 ) {
homeVisitListHTML =
`
<tr>
@ -375,6 +394,48 @@
xhr.send(JSON.stringify(jsonData));
});
}
const trainingDatetimeInput = document.getElementById('training-datetime');
const regEndDateInput = document.getElementById('reg-end-date');
trainingDatetimeInput.addEventListener('change', function() {
const selectedDatetime = new Date(this.value);
const year = selectedDatetime.getFullYear();
const month = ('0' + (selectedDatetime.getMonth() + 1)).slice(-2);
const day = ('0' + selectedDatetime.getDate()).slice(-2);
const formattedDate = year + '-' + month + '-' + day;
regEndDateInput.value = formattedDate;
let [date, time] = this.value.split('T');
time = time.split(':');
time[1] = '00'; // 분을 00으로 설정
const newDatetime = date + 'T' + time.join(':');
this.value = newDatetime;
});
{
const regStartDateInput = document.getElementById('reg-start-date');
const today = new Date();
const year = today.getFullYear();
const month = ('0' + (today.getMonth() + 1)).slice(-2);
const day = ('0' + today.getDate()).slice(-2);
const formattedDate = year + '-' + month + '-' + day;
regStartDateInput.value = formattedDate;
}
updateList();
});
</script>
@ -433,10 +494,10 @@
</thead>
<tbody class="new-course-creation-tbody">
<tr>
<td><input type="text" value="2024년 11월 집합교육" id="training-name" name="training-name" class="training-name" placeholder="교육명을 입력하세요" /></td>
<td><input type="datetime-local" value="2024-11-04T14:00" id="training-datetime" name="training-datetime" class="training-datetime" placeholder="" /></td>
<td><input type="text" value="건설기술연구원 대회의실" id="training-location" name="training-location" class="training-location" placeholder="교육장소를 입력하세요" /></td>
<td><input type="date" value="2024-10-24" id="reg-start-date" name="reg-start-date" class="reg-start-date reg-start-end-date" placeholder="" /> ~ <input type="date" value="2024-11-04" id="reg-end-date" name="reg-end-date" class="reg-end-date reg-start-end-date" placeholder="" /></td>
<td><input type="text" id="training-name" name="training-name" class="training-name" placeholder="교육명을 입력하세요" /></td>
<td><input type="datetime-local" id="training-datetime" name="training-datetime" class="training-datetime" placeholder="" /></td>
<td><input type="text" id="training-location" name="training-location" class="training-location" placeholder="교육장소를 입력하세요" /></td>
<td><input type="date" id="reg-start-date" name="reg-start-date" class="reg-start-date reg-start-end-date" placeholder="" /> ~ <input type="date" value="2024-11-04" id="reg-end-date" name="reg-end-date" class="reg-end-date reg-start-end-date" placeholder="" /></td>
<td><button id="request-registry-button" class="register">등록</button></td>
</tr>
</tbody>

View File

@ -0,0 +1,12 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../detect-libc/bin/detect-libc.js" "$@"
else
exec node "$basedir/../detect-libc/bin/detect-libc.js" "$@"
fi

View File

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\detect-libc\bin\detect-libc.js" %*

View File

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../detect-libc/bin/detect-libc.js" $args
} else {
& "$basedir/node$exe" "$basedir/../detect-libc/bin/detect-libc.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../detect-libc/bin/detect-libc.js" $args
} else {
& "node$exe" "$basedir/../detect-libc/bin/detect-libc.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

View File

@ -4,27 +4,55 @@
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"node_modules/@parcel/watcher": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz",
"integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"engines": {
"node": ">=8"
"node": ">= 10.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.4.1",
"@parcel/watcher-darwin-arm64": "2.4.1",
"@parcel/watcher-darwin-x64": "2.4.1",
"@parcel/watcher-freebsd-x64": "2.4.1",
"@parcel/watcher-linux-arm-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-musl": "2.4.1",
"@parcel/watcher-linux-x64-glibc": "2.4.1",
"@parcel/watcher-linux-x64-musl": "2.4.1",
"@parcel/watcher-win32-arm64": "2.4.1",
"@parcel/watcher-win32-ia32": "2.4.1",
"@parcel/watcher-win32-x64": "2.4.1"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz",
"integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/braces": {
@ -39,26 +67,28 @@
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 8.10.0"
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
"engines": {
"node": ">=0.10"
}
},
"node_modules/fill-range": {
@ -72,32 +102,10 @@
"node": ">=8"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/immutable": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz",
"integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ=="
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
"integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw=="
},
"node_modules/is-extglob": {
"version": "2.1.1",
@ -126,14 +134,23 @@
"node": ">=0.12.0"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=0.10.0"
"node": ">=8.6"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@ -146,22 +163,24 @@
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": {
"picomatch": "^2.2.1"
},
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
"engines": {
"node": ">=8.10.0"
"node": ">= 14.16.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sass": {
"version": "1.77.4",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.4.tgz",
"integrity": "sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==",
"version": "1.80.4",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.80.4.tgz",
"integrity": "sha512-rhMQ2tSF5CsuuspvC94nPM9rToiAFw2h3JTrLlgmNw1MH79v8Cr3DH6KF6o6r+8oofY3iYVPUf66KzC8yuVN1w==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"@parcel/watcher": "^2.4.1",
"chokidar": "^4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
@ -173,9 +192,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"engines": {
"node": ">=0.10.0"
}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present Devon Govett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1 @@
This is the win32-x64 build of @parcel/watcher. See https://github.com/parcel-bundler/watcher for details.

View File

@ -0,0 +1,30 @@
{
"name": "@parcel/watcher-win32-x64",
"version": "2.4.1",
"main": "watcher.node",
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/watcher.git"
},
"description": "A native C++ Node module for querying and subscribing to filesystem events. Used by Parcel 2.",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"files": [
"watcher.node"
],
"engines": {
"node": ">= 10.0.0"
},
"os": [
"win32"
],
"cpu": [
"x64"
]
}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present Devon Govett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,135 @@
# @parcel/watcher
A native C++ Node module for querying and subscribing to filesystem events. Used by [Parcel 2](https://github.com/parcel-bundler/parcel).
## Features
- **Watch** - subscribe to realtime recursive directory change notifications when files or directories are created, updated, or deleted.
- **Query** - performantly query for historical change events in a directory, even when your program is not running.
- **Native** - implemented in C++ for performance and low-level integration with the operating system.
- **Cross platform** - includes backends for macOS, Linux, Windows, FreeBSD, and Watchman.
- **Performant** - events are throttled in C++ so the JavaScript thread is not overwhelmed during large filesystem changes (e.g. `git checkout` or `npm install`).
- **Scalable** - tens of thousands of files can be watched or queried at once with good performance.
## Example
```javascript
const watcher = require('@parcel/watcher');
const path = require('path');
// Subscribe to events
let subscription = await watcher.subscribe(process.cwd(), (err, events) => {
console.log(events);
});
// later on...
await subscription.unsubscribe();
// Get events since some saved snapshot in the past
let snapshotPath = path.join(process.cwd(), 'snapshot.txt');
let events = await watcher.getEventsSince(process.cwd(), snapshotPath);
// Save a snapshot for later
await watcher.writeSnapshot(process.cwd(), snapshotPath);
```
## Watching
`@parcel/watcher` supports subscribing to realtime notifications of changes in a directory. It works recursively, so changes in sub-directories will also be emitted.
Events are throttled and coalesced for performance during large changes like `git checkout` or `npm install`, and a single notification will be emitted with all of the events at the end.
Only one notification will be emitted per file. For example, if a file was both created and updated since the last event, you'll get only a `create` event. If a file is both created and deleted, you will not be notifed of that file. Renames cause two events: a `delete` for the old name, and a `create` for the new name.
```javascript
let subscription = await watcher.subscribe(process.cwd(), (err, events) => {
console.log(events);
});
```
Events have two properties:
- `type` - the event type: `create`, `update`, or `delete`.
- `path` - the absolute path to the file or directory.
To unsubscribe from change notifications, call the `unsubscribe` method on the returned subscription object.
```javascript
await subscription.unsubscribe();
```
`@parcel/watcher` has the following watcher backends, listed in priority order:
- [FSEvents](https://developer.apple.com/documentation/coreservices/file_system_events) on macOS
- [Watchman](https://facebook.github.io/watchman/) if installed
- [inotify](http://man7.org/linux/man-pages/man7/inotify.7.html) on Linux
- [ReadDirectoryChangesW](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v%3Dvs.85%29.aspx) on Windows
- [kqueue](https://man.freebsd.org/cgi/man.cgi?kqueue) on FreeBSD, or as an alternative to FSEvents on macOS
You can specify the exact backend you wish to use by passing the `backend` option. If that backend is not available on the current platform, the default backend will be used instead. See below for the list of backend names that can be passed to the options.
## Querying
`@parcel/watcher` also supports querying for historical changes made in a directory, even when your program is not running. This makes it easy to invalidate a cache and re-build only the files that have changed, for example. It can be **significantly** faster than traversing the entire filesystem to determine what files changed, depending on the platform.
In order to query for historical changes, you first need a previous snapshot to compare to. This can be saved to a file with the `writeSnapshot` function, e.g. just before your program exits.
```javascript
await watcher.writeSnapshot(dirPath, snapshotPath);
```
When your program starts up, you can query for changes that have occurred since that snapshot using the `getEventsSince` function.
```javascript
let events = await watcher.getEventsSince(dirPath, snapshotPath);
```
The events returned are exactly the same as the events that would be passed to the `subscribe` callback (see above).
`@parcel/watcher` has the following watcher backends, listed in priority order:
- [FSEvents](https://developer.apple.com/documentation/coreservices/file_system_events) on macOS
- [Watchman](https://facebook.github.io/watchman/) if installed
- [fts](http://man7.org/linux/man-pages/man3/fts.3.html) (brute force) on Linux and FreeBSD
- [FindFirstFile](https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilea) (brute force) on Windows
The FSEvents (macOS) and Watchman backends are significantly more performant than the brute force backends used by default on Linux and Windows, for example returning results in miliseconds instead of seconds for large directory trees. This is because a background daemon monitoring filesystem changes on those platforms allows us to query cached data rather than traversing the filesystem manually (brute force).
macOS has good performance with FSEvents by default. For the best performance on other platforms, install [Watchman](https://facebook.github.io/watchman/) and it will be used by `@parcel/watcher` automatically.
You can specify the exact backend you wish to use by passing the `backend` option. If that backend is not available on the current platform, the default backend will be used instead. See below for the list of backend names that can be passed to the options.
## Options
All of the APIs in `@parcel/watcher` support the following options, which are passed as an object as the last function argument.
- `ignore` - an array of paths or glob patterns to ignore. uses [`is-glob`](https://github.com/micromatch/is-glob) to distinguish paths from globs. glob patterns are parsed with [`micromatch`](https://github.com/micromatch/micromatch) (see [features](https://github.com/micromatch/micromatch#matching-features)).
- paths can be relative or absolute and can either be files or directories. No events will be emitted about these files or directories or their children.
- glob patterns match on relative paths from the root that is watched. No events will be emitted for matching paths.
- `backend` - the name of an explicitly chosen backend to use. Allowed options are `"fs-events"`, `"watchman"`, `"inotify"`, `"kqueue"`, `"windows"`, or `"brute-force"` (only for querying). If the specified backend is not available on the current platform, the default backend will be used instead.
## WASM
The `@parcel/watcher-wasm` package can be used in place of `@parcel/watcher` on unsupported platforms. It relies on the Node `fs` module, so in non-Node environments such as browsers, an `fs` polyfill will be needed.
**Note**: the WASM implementation is significantly less efficient than the native implementations because it must crawl the file system to watch each directory individually. Use the native `@parcel/watcher` package wherever possible.
```js
import {subscribe} from '@parcel/watcher-wasm';
// Use the module as documented above.
subscribe(/* ... */);
```
## Who is using this?
- [Parcel 2](https://parceljs.org/)
- [VSCode](https://code.visualstudio.com/updates/v1_62#_file-watching-changes)
- [Tailwind CSS Intellisense](https://github.com/tailwindlabs/tailwindcss-intellisense)
- [Gatsby Cloud](https://twitter.com/chatsidhartha/status/1435647412828196867)
- [Nx](https://nx.dev)
- [Nuxt](https://nuxt.com)
## License
MIT

View File

@ -0,0 +1,49 @@
declare type FilePath = string;
declare type GlobPattern = string;
declare namespace ParcelWatcher {
export type BackendType =
| 'fs-events'
| 'watchman'
| 'inotify'
| 'windows'
| 'brute-force';
export type EventType = 'create' | 'update' | 'delete';
export interface Options {
ignore?: (FilePath|GlobPattern)[];
backend?: BackendType;
}
export type SubscribeCallback = (
err: Error | null,
events: Event[]
) => unknown;
export interface AsyncSubscription {
unsubscribe(): Promise<void>;
}
export interface Event {
path: FilePath;
type: EventType;
}
export function getEventsSince(
dir: FilePath,
snapshot: FilePath,
opts?: Options
): Promise<Event[]>;
export function subscribe(
dir: FilePath,
fn: SubscribeCallback,
opts?: Options
): Promise<AsyncSubscription>;
export function unsubscribe(
dir: FilePath,
fn: SubscribeCallback,
opts?: Options
): Promise<void>;
export function writeSnapshot(
dir: FilePath,
snapshot: FilePath,
opts?: Options
): Promise<FilePath>;
}
export = ParcelWatcher;

View File

@ -0,0 +1,41 @@
const {createWrapper} = require('./wrapper');
let name = `@parcel/watcher-${process.platform}-${process.arch}`;
if (process.platform === 'linux') {
const { MUSL, family } = require('detect-libc');
if (family === MUSL) {
name += '-musl';
} else {
name += '-glibc';
}
}
let binding;
try {
binding = require(name);
} catch (err) {
handleError(err);
try {
binding = require('./build/Release/watcher.node');
} catch (err) {
handleError(err);
try {
binding = require('./build/Debug/watcher.node');
} catch (err) {
handleError(err);
throw new Error(`No prebuild or local build of @parcel/watcher found. Tried ${name}. Please ensure it is installed (don't use --no-optional when installing with npm). Otherwise it is possible we don't support your platform yet. If this is the case, please report an issue to https://github.com/parcel-bundler/watcher.`);
}
}
}
function handleError(err) {
if (err?.code !== 'MODULE_NOT_FOUND') {
throw err;
}
}
const wrapper = createWrapper(binding);
exports.writeSnapshot = wrapper.writeSnapshot;
exports.getEventsSince = wrapper.getEventsSince;
exports.subscribe = wrapper.subscribe;
exports.unsubscribe = wrapper.unsubscribe;

View File

@ -0,0 +1,48 @@
// @flow
declare type FilePath = string;
declare type GlobPattern = string;
export type BackendType =
| 'fs-events'
| 'watchman'
| 'inotify'
| 'windows'
| 'brute-force';
export type EventType = 'create' | 'update' | 'delete';
export interface Options {
ignore?: Array<FilePath | GlobPattern>,
backend?: BackendType
}
export type SubscribeCallback = (
err: ?Error,
events: Array<Event>
) => mixed;
export interface AsyncSubscription {
unsubscribe(): Promise<void>
}
export interface Event {
path: FilePath,
type: EventType
}
declare module.exports: {
getEventsSince(
dir: FilePath,
snapshot: FilePath,
opts?: Options
): Promise<Array<Event>>,
subscribe(
dir: FilePath,
fn: SubscribeCallback,
opts?: Options
): Promise<AsyncSubscription>,
unsubscribe(
dir: FilePath,
fn: SubscribeCallback,
opts?: Options
): Promise<void>,
writeSnapshot(
dir: FilePath,
snapshot: FilePath,
opts?: Options
): Promise<FilePath>
}

View File

@ -0,0 +1,83 @@
{
"name": "@parcel/watcher",
"version": "2.4.1",
"main": "index.js",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/watcher.git"
},
"description": "A native C++ Node module for querying and subscribing to filesystem events. Used by Parcel 2.",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"files": [
"index.js",
"index.js.flow",
"index.d.ts",
"wrapper.js",
"package.json",
"README.md",
"LICENSE"
],
"scripts": {
"prebuild": "prebuildify --napi --strip --tag-libc",
"format": "prettier --write \"./**/*.{js,json,md}\"",
"build": "node-gyp rebuild -j 8 --debug --verbose",
"test": "mocha"
},
"engines": {
"node": ">= 10.0.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,json,md}": [
"prettier --write",
"git add"
]
},
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"devDependencies": {
"esbuild": "^0.19.8",
"fs-extra": "^10.0.0",
"husky": "^7.0.2",
"lint-staged": "^11.1.2",
"mocha": "^9.1.1",
"napi-wasm": "^1.1.0",
"prebuildify": "^5.0.1",
"prettier": "^2.3.2"
},
"binary": {
"napi_versions": [
3
]
},
"optionalDependencies": {
"@parcel/watcher-darwin-x64": "2.4.1",
"@parcel/watcher-darwin-arm64": "2.4.1",
"@parcel/watcher-win32-x64": "2.4.1",
"@parcel/watcher-win32-arm64": "2.4.1",
"@parcel/watcher-win32-ia32": "2.4.1",
"@parcel/watcher-linux-x64-glibc": "2.4.1",
"@parcel/watcher-linux-x64-musl": "2.4.1",
"@parcel/watcher-linux-arm64-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-musl": "2.4.1",
"@parcel/watcher-linux-arm-glibc": "2.4.1",
"@parcel/watcher-android-arm64": "2.4.1",
"@parcel/watcher-freebsd-x64": "2.4.1"
}
}

View File

@ -0,0 +1,77 @@
const path = require('path');
const micromatch = require('micromatch');
const isGlob = require('is-glob');
function normalizeOptions(dir, opts = {}) {
const { ignore, ...rest } = opts;
if (Array.isArray(ignore)) {
opts = { ...rest };
for (const value of ignore) {
if (isGlob(value)) {
if (!opts.ignoreGlobs) {
opts.ignoreGlobs = [];
}
const regex = micromatch.makeRe(value, {
// We set `dot: true` to workaround an issue with the
// regular expression on Linux where the resulting
// negative lookahead `(?!(\\/|^)` was never matching
// in some cases. See also https://bit.ly/3UZlQDm
dot: true,
// C++ does not support lookbehind regex patterns, they
// were only added later to JavaScript engines
// (https://bit.ly/3V7S6UL)
lookbehinds: false
});
opts.ignoreGlobs.push(regex.source);
} else {
if (!opts.ignorePaths) {
opts.ignorePaths = [];
}
opts.ignorePaths.push(path.resolve(dir, value));
}
}
}
return opts;
}
exports.createWrapper = (binding) => {
return {
writeSnapshot(dir, snapshot, opts) {
return binding.writeSnapshot(
path.resolve(dir),
path.resolve(snapshot),
normalizeOptions(dir, opts),
);
},
getEventsSince(dir, snapshot, opts) {
return binding.getEventsSince(
path.resolve(dir),
path.resolve(snapshot),
normalizeOptions(dir, opts),
);
},
async subscribe(dir, fn, opts) {
dir = path.resolve(dir);
opts = normalizeOptions(dir, opts);
await binding.subscribe(dir, fn, opts);
return {
unsubscribe() {
return binding.unsubscribe(dir, fn, opts);
},
};
},
unsubscribe(dir, fn, opts) {
return binding.unsubscribe(
path.resolve(dir),
fn,
normalizeOptions(dir, opts),
);
}
};
};

View File

@ -1,15 +0,0 @@
The ISC License
Copyright (c) 2019 Elan Shanker, Paul Miller (https://paulmillr.com)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,87 +0,0 @@
anymatch [![Build Status](https://travis-ci.org/micromatch/anymatch.svg?branch=master)](https://travis-ci.org/micromatch/anymatch) [![Coverage Status](https://img.shields.io/coveralls/micromatch/anymatch.svg?branch=master)](https://coveralls.io/r/micromatch/anymatch?branch=master)
======
Javascript module to match a string against a regular expression, glob, string,
or function that takes the string as an argument and returns a truthy or falsy
value. The matcher can also be an array of any or all of these. Useful for
allowing a very flexible user-defined config to define things like file paths.
__Note: This module has Bash-parity, please be aware that Windows-style backslashes are not supported as separators. See https://github.com/micromatch/micromatch#backslashes for more information.__
Usage
-----
```sh
npm install anymatch
```
#### anymatch(matchers, testString, [returnIndex], [options])
* __matchers__: (_Array|String|RegExp|Function_)
String to be directly matched, string with glob patterns, regular expression
test, function that takes the testString as an argument and returns a truthy
value if it should be matched, or an array of any number and mix of these types.
* __testString__: (_String|Array_) The string to test against the matchers. If
passed as an array, the first element of the array will be used as the
`testString` for non-function matchers, while the entire array will be applied
as the arguments for function matchers.
* __options__: (_Object_ [optional]_) Any of the [picomatch](https://github.com/micromatch/picomatch#options) options.
* __returnIndex__: (_Boolean [optional]_) If true, return the array index of
the first matcher that that testString matched, or -1 if no match, instead of a
boolean result.
```js
const anymatch = require('anymatch');
const matchers = [ 'path/to/file.js', 'path/anyjs/**/*.js', /foo.js$/, string => string.includes('bar') && string.length > 10 ] ;
anymatch(matchers, 'path/to/file.js'); // true
anymatch(matchers, 'path/anyjs/baz.js'); // true
anymatch(matchers, 'path/to/foo.js'); // true
anymatch(matchers, 'path/to/bar.js'); // true
anymatch(matchers, 'bar.js'); // false
// returnIndex = true
anymatch(matchers, 'foo.js', {returnIndex: true}); // 2
anymatch(matchers, 'path/anyjs/foo.js', {returnIndex: true}); // 1
// any picomatc
// using globs to match directories and their children
anymatch('node_modules', 'node_modules'); // true
anymatch('node_modules', 'node_modules/somelib/index.js'); // false
anymatch('node_modules/**', 'node_modules/somelib/index.js'); // true
anymatch('node_modules/**', '/absolute/path/to/node_modules/somelib/index.js'); // false
anymatch('**/node_modules/**', '/absolute/path/to/node_modules/somelib/index.js'); // true
const matcher = anymatch(matchers);
['foo.js', 'bar.js'].filter(matcher); // [ 'foo.js' ]
anymatch master*
```
#### anymatch(matchers)
You can also pass in only your matcher(s) to get a curried function that has
already been bound to the provided matching criteria. This can be used as an
`Array#filter` callback.
```js
var matcher = anymatch(matchers);
matcher('path/to/file.js'); // true
matcher('path/anyjs/baz.js', true); // 1
['foo.js', 'bar.js'].filter(matcher); // ['foo.js']
```
Changelog
----------
[See release notes page on GitHub](https://github.com/micromatch/anymatch/releases)
- **v3.0:** Removed `startIndex` and `endIndex` arguments. Node 8.x-only.
- **v2.0:** [micromatch](https://github.com/jonschlinkert/micromatch) moves away from minimatch-parity and inline with Bash. This includes handling backslashes differently (see https://github.com/micromatch/micromatch#backslashes for more information).
- **v1.2:** anymatch uses [micromatch](https://github.com/jonschlinkert/micromatch)
for glob pattern matching. Issues with glob pattern matching should be
reported directly to the [micromatch issue tracker](https://github.com/jonschlinkert/micromatch/issues).
License
-------
[ISC](https://raw.github.com/micromatch/anymatch/master/LICENSE)

View File

@ -1,20 +0,0 @@
type AnymatchFn = (testString: string) => boolean;
type AnymatchPattern = string|RegExp|AnymatchFn;
type AnymatchMatcher = AnymatchPattern|AnymatchPattern[]
type AnymatchTester = {
(testString: string|any[], returnIndex: true): number;
(testString: string|any[]): boolean;
}
type PicomatchOptions = {dot: boolean};
declare const anymatch: {
(matchers: AnymatchMatcher): AnymatchTester;
(matchers: AnymatchMatcher, testString: null, returnIndex: true | PicomatchOptions): AnymatchTester;
(matchers: AnymatchMatcher, testString: string|any[], returnIndex: true | PicomatchOptions): number;
(matchers: AnymatchMatcher, testString: string|any[]): boolean;
}
export {AnymatchMatcher as Matcher}
export {AnymatchTester as Tester}
export default anymatch

View File

@ -1,104 +0,0 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
const picomatch = require('picomatch');
const normalizePath = require('normalize-path');
/**
* @typedef {(testString: string) => boolean} AnymatchFn
* @typedef {string|RegExp|AnymatchFn} AnymatchPattern
* @typedef {AnymatchPattern|AnymatchPattern[]} AnymatchMatcher
*/
const BANG = '!';
const DEFAULT_OPTIONS = {returnIndex: false};
const arrify = (item) => Array.isArray(item) ? item : [item];
/**
* @param {AnymatchPattern} matcher
* @param {object} options
* @returns {AnymatchFn}
*/
const createPattern = (matcher, options) => {
if (typeof matcher === 'function') {
return matcher;
}
if (typeof matcher === 'string') {
const glob = picomatch(matcher, options);
return (string) => matcher === string || glob(string);
}
if (matcher instanceof RegExp) {
return (string) => matcher.test(string);
}
return (string) => false;
};
/**
* @param {Array<Function>} patterns
* @param {Array<Function>} negPatterns
* @param {String|Array} args
* @param {Boolean} returnIndex
* @returns {boolean|number}
*/
const matchPatterns = (patterns, negPatterns, args, returnIndex) => {
const isList = Array.isArray(args);
const _path = isList ? args[0] : args;
if (!isList && typeof _path !== 'string') {
throw new TypeError('anymatch: second argument must be a string: got ' +
Object.prototype.toString.call(_path))
}
const path = normalizePath(_path, false);
for (let index = 0; index < negPatterns.length; index++) {
const nglob = negPatterns[index];
if (nglob(path)) {
return returnIndex ? -1 : false;
}
}
const applied = isList && [path].concat(args.slice(1));
for (let index = 0; index < patterns.length; index++) {
const pattern = patterns[index];
if (isList ? pattern(...applied) : pattern(path)) {
return returnIndex ? index : true;
}
}
return returnIndex ? -1 : false;
};
/**
* @param {AnymatchMatcher} matchers
* @param {Array|string} testString
* @param {object} options
* @returns {boolean|number|Function}
*/
const anymatch = (matchers, testString, options = DEFAULT_OPTIONS) => {
if (matchers == null) {
throw new TypeError('anymatch: specify first argument');
}
const opts = typeof options === 'boolean' ? {returnIndex: options} : options;
const returnIndex = opts.returnIndex || false;
// Early cache for matchers.
const mtchers = arrify(matchers);
const negatedGlobs = mtchers
.filter(item => typeof item === 'string' && item.charAt(0) === BANG)
.map(item => item.slice(1))
.map(item => picomatch(item, opts));
const patterns = mtchers
.filter(item => typeof item !== 'string' || (typeof item === 'string' && item.charAt(0) !== BANG))
.map(matcher => createPattern(matcher, opts));
if (testString == null) {
return (testString, ri = false) => {
const returnIndex = typeof ri === 'boolean' ? ri : false;
return matchPatterns(patterns, negatedGlobs, testString, returnIndex);
}
}
return matchPatterns(patterns, negatedGlobs, testString, returnIndex);
};
anymatch.default = anymatch;
module.exports = anymatch;

View File

@ -1,48 +0,0 @@
{
"name": "anymatch",
"version": "3.1.3",
"description": "Matches strings against configurable strings, globs, regular expressions, and/or functions",
"files": [
"index.js",
"index.d.ts"
],
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"author": {
"name": "Elan Shanker",
"url": "https://github.com/es128"
},
"license": "ISC",
"homepage": "https://github.com/micromatch/anymatch",
"repository": {
"type": "git",
"url": "https://github.com/micromatch/anymatch"
},
"keywords": [
"match",
"any",
"string",
"file",
"fs",
"list",
"glob",
"regex",
"regexp",
"regular",
"expression",
"function"
],
"scripts": {
"test": "nyc mocha",
"mocha": "mocha"
},
"devDependencies": {
"mocha": "^6.1.3",
"nyc": "^14.0.0"
},
"engines": {
"node": ">= 8"
}
}

View File

@ -1,263 +0,0 @@
[
"3dm",
"3ds",
"3g2",
"3gp",
"7z",
"a",
"aac",
"adp",
"afdesign",
"afphoto",
"afpub",
"ai",
"aif",
"aiff",
"alz",
"ape",
"apk",
"appimage",
"ar",
"arj",
"asf",
"au",
"avi",
"bak",
"baml",
"bh",
"bin",
"bk",
"bmp",
"btif",
"bz2",
"bzip2",
"cab",
"caf",
"cgm",
"class",
"cmx",
"cpio",
"cr2",
"cur",
"dat",
"dcm",
"deb",
"dex",
"djvu",
"dll",
"dmg",
"dng",
"doc",
"docm",
"docx",
"dot",
"dotm",
"dra",
"DS_Store",
"dsk",
"dts",
"dtshd",
"dvb",
"dwg",
"dxf",
"ecelp4800",
"ecelp7470",
"ecelp9600",
"egg",
"eol",
"eot",
"epub",
"exe",
"f4v",
"fbs",
"fh",
"fla",
"flac",
"flatpak",
"fli",
"flv",
"fpx",
"fst",
"fvt",
"g3",
"gh",
"gif",
"graffle",
"gz",
"gzip",
"h261",
"h263",
"h264",
"icns",
"ico",
"ief",
"img",
"ipa",
"iso",
"jar",
"jpeg",
"jpg",
"jpgv",
"jpm",
"jxr",
"key",
"ktx",
"lha",
"lib",
"lvp",
"lz",
"lzh",
"lzma",
"lzo",
"m3u",
"m4a",
"m4v",
"mar",
"mdi",
"mht",
"mid",
"midi",
"mj2",
"mka",
"mkv",
"mmr",
"mng",
"mobi",
"mov",
"movie",
"mp3",
"mp4",
"mp4a",
"mpeg",
"mpg",
"mpga",
"mxu",
"nef",
"npx",
"numbers",
"nupkg",
"o",
"odp",
"ods",
"odt",
"oga",
"ogg",
"ogv",
"otf",
"ott",
"pages",
"pbm",
"pcx",
"pdb",
"pdf",
"pea",
"pgm",
"pic",
"png",
"pnm",
"pot",
"potm",
"potx",
"ppa",
"ppam",
"ppm",
"pps",
"ppsm",
"ppsx",
"ppt",
"pptm",
"pptx",
"psd",
"pya",
"pyc",
"pyo",
"pyv",
"qt",
"rar",
"ras",
"raw",
"resources",
"rgb",
"rip",
"rlc",
"rmf",
"rmvb",
"rpm",
"rtf",
"rz",
"s3m",
"s7z",
"scpt",
"sgi",
"shar",
"snap",
"sil",
"sketch",
"slk",
"smv",
"snk",
"so",
"stl",
"suo",
"sub",
"swf",
"tar",
"tbz",
"tbz2",
"tga",
"tgz",
"thmx",
"tif",
"tiff",
"tlz",
"ttc",
"ttf",
"txz",
"udf",
"uvh",
"uvi",
"uvm",
"uvp",
"uvs",
"uvu",
"viv",
"vob",
"war",
"wav",
"wax",
"wbmp",
"wdp",
"weba",
"webm",
"webp",
"whl",
"wim",
"wm",
"wma",
"wmv",
"wmx",
"woff",
"woff2",
"wrm",
"wvx",
"xbm",
"xif",
"xla",
"xlam",
"xls",
"xlsb",
"xlsm",
"xlsx",
"xlt",
"xltm",
"xltx",
"xm",
"xmind",
"xpi",
"xpm",
"xwd",
"xz",
"z",
"zip",
"zipx"
]

View File

@ -1,3 +0,0 @@
declare const binaryExtensionsJson: readonly string[];
export = binaryExtensionsJson;

View File

@ -1,14 +0,0 @@
/**
List of binary file extensions.
@example
```
import binaryExtensions = require('binary-extensions');
console.log(binaryExtensions);
//=> ['3ds', '3g2', …]
```
*/
declare const binaryExtensions: readonly string[];
export = binaryExtensions;

View File

@ -1 +0,0 @@
module.exports = require('./binary-extensions.json');

View File

@ -1,10 +0,0 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
Copyright (c) Paul Miller (https://paulmillr.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,40 +0,0 @@
{
"name": "binary-extensions",
"version": "2.3.0",
"description": "List of binary file extensions",
"license": "MIT",
"repository": "sindresorhus/binary-extensions",
"funding": "https://github.com/sponsors/sindresorhus",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"sideEffects": false,
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd"
},
"files": [
"index.js",
"index.d.ts",
"binary-extensions.json",
"binary-extensions.json.d.ts"
],
"keywords": [
"binary",
"extensions",
"extension",
"file",
"json",
"list",
"array"
],
"devDependencies": {
"ava": "^1.4.1",
"tsd": "^0.7.2",
"xo": "^0.24.0"
}
}

View File

@ -1,25 +0,0 @@
# binary-extensions
> List of binary file extensions
The list is just a [JSON file](binary-extensions.json) and can be used anywhere.
## Install
```sh
npm install binary-extensions
```
## Usage
```js
const binaryExtensions = require('binary-extensions');
console.log(binaryExtensions);
//=> ['3ds', '3g2', …]
```
## Related
- [is-binary-path](https://github.com/sindresorhus/is-binary-path) - Check if a filepath is a binary file
- [text-extensions](https://github.com/sindresorhus/text-extensions) - List of text file extensions

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker
Copyright (c) 2012 Paul Miller (https://paulmillr.com), Elan Shanker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal

View File

@ -1,57 +1,40 @@
# Chokidar [![Weekly downloads](https://img.shields.io/npm/dw/chokidar.svg)](https://github.com/paulmillr/chokidar) [![Yearly downloads](https://img.shields.io/npm/dy/chokidar.svg)](https://github.com/paulmillr/chokidar)
# Chokidar [![Weekly downloads](https://img.shields.io/npm/dw/chokidar.svg)](https://github.com/paulmillr/chokidar)
> Minimal and efficient cross-platform file watching library
[![NPM](https://nodei.co/npm/chokidar.png)](https://www.npmjs.com/package/chokidar)
## Why?
Node.js `fs.watch`:
There are many reasons to prefer Chokidar to raw fs.watch / fs.watchFile in 2024:
* Doesn't report filenames on MacOS.
* Doesn't report events at all when using editors like Sublime on MacOS.
* Often reports events twice.
* Emits most changes as `rename`.
* Does not provide an easy way to recursively watch file trees.
* Does not support recursive watching on Linux.
- Events are properly reported
- macOS events report filenames
- events are not reported twice
- changes are reported as add / change / unlink instead of useless `rename`
- Atomic writes are supported, using `atomic` option
- Some file editors use them
- Chunked writes are supported, using `awaitWriteFinish` option
- Large files are commonly written in chunks
- File / dir filtering is supported
- Symbolic links are supported
- Recursive watching is always supported, instead of partial when using raw events
- Includes a way to limit recursion depth
Node.js `fs.watchFile`:
* Almost as bad at event handling.
* Also does not provide any recursive watching.
* Results in high CPU utilization.
Chokidar resolves these problems.
Initially made for **[Brunch](https://brunch.io/)** (an ultra-swift web app build tool), it is now used in
[Microsoft's Visual Studio Code](https://github.com/microsoft/vscode),
[gulp](https://github.com/gulpjs/gulp/),
[karma](https://karma-runner.github.io/),
[PM2](https://github.com/Unitech/PM2),
[browserify](http://browserify.org/),
[webpack](https://webpack.github.io/),
[BrowserSync](https://www.browsersync.io/),
and [many others](https://www.npmjs.com/browse/depended/chokidar).
It has proven itself in production environments.
Version 3 is out! Check out our blog post about it: [Chokidar 3: How to save 32TB of traffic every week](https://paulmillr.com/posts/chokidar-3-save-32tb-of-traffic/)
## How?
Chokidar does still rely on the Node.js core `fs` module, but when using
Chokidar relies on the Node.js core `fs` module, but when using
`fs.watch` and `fs.watchFile` for watching, it normalizes the events it
receives, often checking for truth by getting file stats and/or dir contents.
On MacOS, chokidar by default uses a native extension exposing the Darwin
`FSEvents` API. This provides very efficient recursive watching compared with
implementations like `kqueue` available on most \*nix platforms. Chokidar still
does have to do some work to normalize the events received that way as well.
On most other platforms, the `fs.watch`-based implementation is the default, which
The `fs.watch`-based implementation is the default, which
avoids polling and keeps CPU usage down. Be advised that chokidar will initiate
watchers recursively for everything within scope of the paths that have been
specified, so be judicious about not wasting system resources by watching much
more than needed.
more than needed. For some cases, `fs.watchFile`, which utilizes polling and uses more resources, is used.
Made for [Brunch](https://brunch.io/) in 2012,
it is now used in [~30 million repositories](https://www.npmjs.com/browse/depended/chokidar) and
has proven itself in production environments.
**Sep 2024 update:** v4 is out! It decreases dependency count from 13 to 1, removes
support for globs, adds support for ESM / Common.js modules, and bumps minimum node.js version from v8 to v14.
Check out [upgrading](#upgrading).
## Getting started
@ -61,25 +44,23 @@ Install with npm:
npm install chokidar
```
Then `require` and use it in your code:
Use it in your code:
```javascript
const chokidar = require('chokidar');
import chokidar from 'chokidar';
// One-liner for current directory
chokidar.watch('.').on('all', (event, path) => {
console.log(event, path);
});
```
## API
```javascript
// Example of a more typical implementation structure
// Extended options
// ----------------
// Initialize watcher.
const watcher = chokidar.watch('file, dir, glob, or array', {
ignored: /(^|[\/\\])\../, // ignore dotfiles
const watcher = chokidar.watch('file, dir, or array', {
ignored: (path, stats) => stats?.isFile() && !path.endsWith('.js'), // only watch js files
persistent: true
});
@ -109,41 +90,47 @@ watcher.on('change', (path, stats) => {
// Watch new files.
watcher.add('new-file');
watcher.add(['new-file-2', 'new-file-3', '**/other-file*']);
watcher.add(['new-file-2', 'new-file-3']);
// Get list of actual paths being watched on the filesystem
var watchedPaths = watcher.getWatched();
let watchedPaths = watcher.getWatched();
// Un-watch some files.
await watcher.unwatch('new-file*');
await watcher.unwatch('new-file');
// Stop watching.
// The method is async!
watcher.close().then(() => console.log('closed'));
// Stop watching. The method is async!
await watcher.close().then(() => console.log('closed'));
// Full list of options. See below for descriptions.
// Do not use this example!
chokidar.watch('file', {
persistent: true,
ignored: '*.txt',
ignoreInitial: false,
followSymlinks: true,
cwd: '.',
disableGlobbing: false,
// ignore .txt files
ignored: (file) => file.endsWith('.txt'),
// watch only .txt files
// ignored: (file, _stats) => _stats?.isFile() && !file.endsWith('.txt'),
usePolling: false,
awaitWriteFinish: true, // emit single event when chunked writes are completed
atomic: true // emit proper events when "atomic writes" (mv _tmp file) are used
// The options also allow specifying custom intervals in ms
// awaitWriteFinish: {
// stabilityThreshold: 2000,
// pollInterval: 100
// },
// atomic: 100,
interval: 100,
binaryInterval: 300,
alwaysStat: false,
depth: 99,
awaitWriteFinish: {
stabilityThreshold: 2000,
pollInterval: 100
},
cwd: '.',
depth: 99,
followSymlinks: true,
ignoreInitial: false,
ignorePermissionErrors: false,
atomic: true // or a custom 'atomicity delay', in milliseconds (default 100)
usePolling: false,
alwaysStat: false,
});
```
@ -151,27 +138,19 @@ chokidar.watch('file', {
`chokidar.watch(paths, [options])`
* `paths` (string or array of strings). Paths to files, dirs to be watched
recursively, or glob patterns.
- Note: globs must not contain windows separators (`\`),
because that's how they work by the standard —
you'll need to replace them with forward slashes (`/`).
- Note 2: for additional glob documentation, check out low-level
library: [picomatch](https://github.com/micromatch/picomatch).
recursively.
* `options` (object) Options object as defined below:
#### Persistence
* `persistent` (default: `true`). Indicates whether the process
should continue to run as long as files are being watched. If set to
`false` when using `fsevents` to watch, no more events will be emitted
after `ready`, even if the process continues to run.
should continue to run as long as files are being watched.
#### Path filtering
* `ignored` ([anymatch](https://github.com/es128/anymatch)-compatible definition)
Defines files/paths to be ignored. The whole relative or absolute path is
tested, not just filename. If a function with two arguments is provided, it
gets called twice per path - once with a single argument (the path), second
* `ignored` function, regex, or path. Defines files/paths to be ignored.
The whole relative or absolute path is tested, not just filename. If a function with two arguments
is provided, it gets called twice per path - once with a single argument (the path), second
time with two arguments (the path and the
[`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
object of that path).
@ -182,8 +161,6 @@ symlinks themselves will be watched for changes instead of following
the link references and bubbling events through the link's path.
* `cwd` (no default). The base directory from which watch `paths` are to be
derived. Paths emitted with events will be relative to this.
* `disableGlobbing` (default: `false`). If set to `true` then the strings passed to `.watch()` and `.add()` are treated as
literal path names, even if they look like globs.
#### Performance
@ -201,10 +178,6 @@ to true (1) or false (0) in order to override this option.
* `binaryInterval` (default: `300`). Interval of file system
polling for binary files.
([see list of binary extensions](https://github.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
* `useFsEvents` (default: `true` on MacOS). Whether to use the
`fsevents` watching interface if available. When set to `true` explicitly
and `fsevents` is available this supercedes the `usePolling` setting. When
set to `false` on MacOS, `usePolling: true` becomes the default.
* `alwaysStat` (default: `false`). If relying upon the
[`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
object that may get passed with `add`, `addDir`, and `change` events, set
@ -247,14 +220,14 @@ milliseconds.
`chokidar.watch()` produces an instance of `FSWatcher`. Methods of `FSWatcher`:
* `.add(path / paths)`: Add files, directories, or glob patterns for tracking.
* `.add(path / paths)`: Add files, directories for tracking.
Takes an array of strings or just one string.
* `.on(event, callback)`: Listen for an FS event.
Available events: `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `ready`,
`raw`, `error`.
Additionally `all` is available which gets emitted with the underlying event
name and path for every event other than `ready`, `raw`, and `error`. `raw` is internal, use it carefully.
* `.unwatch(path / paths)`: Stop watching files, directories, or glob patterns.
* `.unwatch(path / paths)`: Stop watching files or directories.
Takes an array of strings or just one string.
* `.close()`: **async** Removes all listeners from watched files. Asynchronous, returns Promise. Use with `await` to ensure bugs don't happen.
* `.getWatched()`: Returns an object representing all the paths on the file
@ -265,37 +238,53 @@ values are arrays of the names of the items contained in each directory.
## CLI
If you need a CLI interface for your file watching, check out
[chokidar-cli](https://github.com/open-cli-tools/chokidar-cli), allowing you to
third party [chokidar-cli](https://github.com/open-cli-tools/chokidar-cli), allowing you to
execute a command on each change, or get a stdio stream of change events.
## Install Troubleshooting
## Troubleshooting
* `npm WARN optional dep failed, continuing fsevents@n.n.n`
* This message is normal part of how `npm` handles optional dependencies and is
not indicative of a problem. Even if accompanied by other related error messages,
Chokidar should function properly.
* `TypeError: fsevents is not a constructor`
* Update chokidar by doing `rm -rf node_modules package-lock.json yarn.lock && npm install`, or update your dependency that uses chokidar.
* Chokidar is producing `ENOSP` error on Linux, like this:
* `bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell`
* On Linux, sometimes there's `ENOSP` error:
* `bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell`
`Error: watch /home/ ENOSPC`
* This means Chokidar ran out of file handles and you'll need to increase their count by executing the following command in Terminal:
* This means Chokidar ran out of file handles and you'll need to increase their count by executing the following command in Terminal:
`echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`
* If using 3.x, upgrade to latest chokidar to prevent fsevents-related issues:
* `npm WARN optional dep failed, continuing fsevents@n.n.n`
* `TypeError: fsevents is not a constructor`
## Changelog
For more detailed changelog, see [`full_changelog.md`](.github/full_changelog.md).
- **v3.5 (Jan 6, 2021):** Support for ARM Macs with Apple Silicon. Fixes for deleted symlinks.
- **v3.4 (Apr 26, 2020):** Support for directory-based symlinks. Fixes for macos file replacement.
- **v3.3 (Nov 2, 2019):** `FSWatcher#close()` method became async. That fixes IO race conditions related to close method.
- **v3.2 (Oct 1, 2019):** Improve Linux RAM usage by 50%. Race condition fixes. Windows glob fixes. Improve stability by using tight range of dependency versions.
- **v3.1 (Sep 16, 2019):** dotfiles are no longer filtered out by default. Use `ignored` option if needed. Improve initial Linux scan time by 50%.
- **v3 (Apr 30, 2019):** massive CPU & RAM consumption improvements; reduces deps / package size by a factor of 17x and bumps Node.js requirement to v8.16 and higher.
- **v2 (Dec 29, 2017):** Globs are now posix-style-only; without windows support. Tons of bugfixes.
- **v1 (Apr 7, 2015):** Glob support, symlink support, tons of bugfixes. Node 0.8+ is supported
- **v0.1 (Apr 20, 2012):** Initial release, extracted from [Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66)
- **v4 (Sep 2024):** remove glob support and bundled fsevents. Decrease dependency count from 13 to 1. Rewrite in typescript. Bumps minimum node.js requirement to v14+
- **v3 (Apr 2019):** massive CPU & RAM consumption improvements; reduces deps / package size by a factor of 17x and bumps Node.js requirement to v8.16+.
- **v2 (Dec 2017):** globs are now posix-style-only. Tons of bugfixes.
- **v1 (Apr 2015):** glob support, symlink support, tons of bugfixes. Node 0.8+ is supported
- **v0.1 (Apr 2012):** Initial release, extracted from [Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66)
### Upgrading
If you've used globs before and want do replicate the functionality with v4:
```js
// v3
chok.watch('**/*.js');
chok.watch("./directory/**/*");
// v4
chok.watch('.', {
ignored: (path, stats) => stats?.isFile() && !path.endsWith('.js'), // only watch js files
});
chok.watch('./directory');
// other way
import { glob } from 'node:fs/promises';
const watcher = watch(await glob('**/*.js'));
// unwatching
// v3
chok.unwatch('**/*.js');
// v4
chok.unwatch(await glob('**/*.js'));
```
## Also

View File

@ -0,0 +1,90 @@
import type { WatchEventType, Stats, FSWatcher as NativeFsWatcher } from 'fs';
import type { FSWatcher, WatchHelper, Throttler } from './index.js';
import type { EntryInfo } from 'readdirp';
export type Path = string;
export declare const STR_DATA = "data";
export declare const STR_END = "end";
export declare const STR_CLOSE = "close";
export declare const EMPTY_FN: () => void;
export declare const IDENTITY_FN: (val: unknown) => unknown;
export declare const isWindows: boolean;
export declare const isMacos: boolean;
export declare const isLinux: boolean;
export declare const isIBMi: boolean;
export declare const EVENTS: {
readonly ALL: "all";
readonly READY: "ready";
readonly ADD: "add";
readonly CHANGE: "change";
readonly ADD_DIR: "addDir";
readonly UNLINK: "unlink";
readonly UNLINK_DIR: "unlinkDir";
readonly RAW: "raw";
readonly ERROR: "error";
};
export type EventName = (typeof EVENTS)[keyof typeof EVENTS];
export type FsWatchContainer = {
listeners: (path: string) => void | Set<any>;
errHandlers: (err: unknown) => void | Set<any>;
rawEmitters: (ev: WatchEventType, path: string, opts: unknown) => void | Set<any>;
watcher: NativeFsWatcher;
watcherUnusable?: boolean;
};
export interface WatchHandlers {
listener: (path: string) => void;
errHandler: (err: unknown) => void;
rawEmitter: (ev: WatchEventType, path: string, opts: unknown) => void;
}
/**
* @mixin
*/
export declare class NodeFsHandler {
fsw: FSWatcher;
_boundHandleError: (error: unknown) => void;
constructor(fsW: FSWatcher);
/**
* Watch file for changes with fs_watchFile or fs_watch.
* @param path to file or dir
* @param listener on fs change
* @returns closer for the watcher instance
*/
_watchWithNodeFs(path: string, listener: (path: string, newStats?: any) => void | Promise<void>): (() => void) | undefined;
/**
* Watch a file and emit add event if warranted.
* @returns closer for the watcher instance
*/
_handleFile(file: Path, stats: Stats, initialAdd: boolean): (() => void) | undefined;
/**
* Handle symlinks encountered while reading a dir.
* @param entry returned by readdirp
* @param directory path of dir being read
* @param path of this item
* @param item basename of this item
* @returns true if no more processing is needed for this entry.
*/
_handleSymlink(entry: EntryInfo, directory: string, path: Path, item: string): Promise<boolean | undefined>;
_handleRead(directory: string, initialAdd: boolean, wh: WatchHelper, target: Path, dir: Path, depth: number, throttler: Throttler): Promise<unknown> | undefined;
/**
* Read directory to add / remove files from `@watched` list and re-read it on change.
* @param dir fs path
* @param stats
* @param initialAdd
* @param depth relative to user-supplied path
* @param target child path targeted for watch
* @param wh Common watch helpers for this path
* @param realpath
* @returns closer for the watcher instance.
*/
_handleDir(dir: string, stats: Stats, initialAdd: boolean, depth: number, target: string, wh: WatchHelper, realpath: string): Promise<(() => void) | undefined>;
/**
* Handle added file, directory, or glob pattern.
* Delegates call to _handleFile / _handleDir after checks.
* @param path to file or ir
* @param initialAdd was the file added at watch instantiation?
* @param priorWh depth relative to user-supplied path
* @param depth Child path actually targeted for watch
* @param target Child path actually targeted for watch
*/
_addToNodeFs(path: string, initialAdd: boolean, priorWh: WatchHelper | undefined, depth: number, target?: string): Promise<string | false | undefined>;
}
//# sourceMappingURL=handler.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,629 @@
import { watchFile, unwatchFile, watch as fs_watch } from 'fs';
import { open, stat, lstat, realpath as fsrealpath } from 'fs/promises';
import * as sysPath from 'path';
import { type as osType } from 'os';
export const STR_DATA = 'data';
export const STR_END = 'end';
export const STR_CLOSE = 'close';
export const EMPTY_FN = () => { };
export const IDENTITY_FN = (val) => val;
const pl = process.platform;
export const isWindows = pl === 'win32';
export const isMacos = pl === 'darwin';
export const isLinux = pl === 'linux';
export const isIBMi = osType() === 'OS400';
export const EVENTS = {
ALL: 'all',
READY: 'ready',
ADD: 'add',
CHANGE: 'change',
ADD_DIR: 'addDir',
UNLINK: 'unlink',
UNLINK_DIR: 'unlinkDir',
RAW: 'raw',
ERROR: 'error',
};
const EV = EVENTS;
const THROTTLE_MODE_WATCH = 'watch';
const statMethods = { lstat, stat };
const KEY_LISTENERS = 'listeners';
const KEY_ERR = 'errHandlers';
const KEY_RAW = 'rawEmitters';
const HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
// prettier-ignore
const binaryExtensions = new Set([
'3dm', '3ds', '3g2', '3gp', '7z', 'a', 'aac', 'adp', 'afdesign', 'afphoto', 'afpub', 'ai',
'aif', 'aiff', 'alz', 'ape', 'apk', 'appimage', 'ar', 'arj', 'asf', 'au', 'avi',
'bak', 'baml', 'bh', 'bin', 'bk', 'bmp', 'btif', 'bz2', 'bzip2',
'cab', 'caf', 'cgm', 'class', 'cmx', 'cpio', 'cr2', 'cur', 'dat', 'dcm', 'deb', 'dex', 'djvu',
'dll', 'dmg', 'dng', 'doc', 'docm', 'docx', 'dot', 'dotm', 'dra', 'DS_Store', 'dsk', 'dts',
'dtshd', 'dvb', 'dwg', 'dxf',
'ecelp4800', 'ecelp7470', 'ecelp9600', 'egg', 'eol', 'eot', 'epub', 'exe',
'f4v', 'fbs', 'fh', 'fla', 'flac', 'flatpak', 'fli', 'flv', 'fpx', 'fst', 'fvt',
'g3', 'gh', 'gif', 'graffle', 'gz', 'gzip',
'h261', 'h263', 'h264', 'icns', 'ico', 'ief', 'img', 'ipa', 'iso',
'jar', 'jpeg', 'jpg', 'jpgv', 'jpm', 'jxr', 'key', 'ktx',
'lha', 'lib', 'lvp', 'lz', 'lzh', 'lzma', 'lzo',
'm3u', 'm4a', 'm4v', 'mar', 'mdi', 'mht', 'mid', 'midi', 'mj2', 'mka', 'mkv', 'mmr', 'mng',
'mobi', 'mov', 'movie', 'mp3',
'mp4', 'mp4a', 'mpeg', 'mpg', 'mpga', 'mxu',
'nef', 'npx', 'numbers', 'nupkg',
'o', 'odp', 'ods', 'odt', 'oga', 'ogg', 'ogv', 'otf', 'ott',
'pages', 'pbm', 'pcx', 'pdb', 'pdf', 'pea', 'pgm', 'pic', 'png', 'pnm', 'pot', 'potm',
'potx', 'ppa', 'ppam',
'ppm', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx', 'psd', 'pya', 'pyc', 'pyo', 'pyv',
'qt',
'rar', 'ras', 'raw', 'resources', 'rgb', 'rip', 'rlc', 'rmf', 'rmvb', 'rpm', 'rtf', 'rz',
's3m', 's7z', 'scpt', 'sgi', 'shar', 'snap', 'sil', 'sketch', 'slk', 'smv', 'snk', 'so',
'stl', 'suo', 'sub', 'swf',
'tar', 'tbz', 'tbz2', 'tga', 'tgz', 'thmx', 'tif', 'tiff', 'tlz', 'ttc', 'ttf', 'txz',
'udf', 'uvh', 'uvi', 'uvm', 'uvp', 'uvs', 'uvu',
'viv', 'vob',
'war', 'wav', 'wax', 'wbmp', 'wdp', 'weba', 'webm', 'webp', 'whl', 'wim', 'wm', 'wma',
'wmv', 'wmx', 'woff', 'woff2', 'wrm', 'wvx',
'xbm', 'xif', 'xla', 'xlam', 'xls', 'xlsb', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx', 'xm',
'xmind', 'xpi', 'xpm', 'xwd', 'xz',
'z', 'zip', 'zipx',
]);
const isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
// TODO: emit errors properly. Example: EMFILE on Macos.
const foreach = (val, fn) => {
if (val instanceof Set) {
val.forEach(fn);
}
else {
fn(val);
}
};
const addAndConvert = (main, prop, item) => {
let container = main[prop];
if (!(container instanceof Set)) {
main[prop] = container = new Set([container]);
}
container.add(item);
};
const clearItem = (cont) => (key) => {
const set = cont[key];
if (set instanceof Set) {
set.clear();
}
else {
delete cont[key];
}
};
const delFromSet = (main, prop, item) => {
const container = main[prop];
if (container instanceof Set) {
container.delete(item);
}
else if (container === item) {
delete main[prop];
}
};
const isEmptySet = (val) => (val instanceof Set ? val.size === 0 : !val);
const FsWatchInstances = new Map();
/**
* Instantiates the fs_watch interface
* @param path to be watched
* @param options to be passed to fs_watch
* @param listener main event handler
* @param errHandler emits info about errors
* @param emitRaw emits raw event data
* @returns {NativeFsWatcher}
*/
function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
const handleEvent = (rawEvent, evPath) => {
listener(path);
emitRaw(rawEvent, evPath, { watchedPath: path });
// emit based on events occurring for files from a directory's watcher in
// case the file's watcher misses it (and rely on throttling to de-dupe)
if (evPath && path !== evPath) {
fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));
}
};
try {
return fs_watch(path, {
persistent: options.persistent,
}, handleEvent);
}
catch (error) {
errHandler(error);
return undefined;
}
}
/**
* Helper for passing fs_watch event data to a collection of listeners
* @param fullPath absolute path bound to fs_watch instance
*/
const fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
const cont = FsWatchInstances.get(fullPath);
if (!cont)
return;
foreach(cont[listenerType], (listener) => {
listener(val1, val2, val3);
});
};
/**
* Instantiates the fs_watch interface or binds listeners
* to an existing one covering the same file system entry
* @param path
* @param fullPath absolute path
* @param options to be passed to fs_watch
* @param handlers container for event listener functions
*/
const setFsWatchListener = (path, fullPath, options, handlers) => {
const { listener, errHandler, rawEmitter } = handlers;
let cont = FsWatchInstances.get(fullPath);
let watcher;
if (!options.persistent) {
watcher = createFsWatchInstance(path, options, listener, errHandler, rawEmitter);
if (!watcher)
return;
return watcher.close.bind(watcher);
}
if (cont) {
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_ERR, errHandler);
addAndConvert(cont, KEY_RAW, rawEmitter);
}
else {
watcher = createFsWatchInstance(path, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, // no need to use broadcast here
fsWatchBroadcast.bind(null, fullPath, KEY_RAW));
if (!watcher)
return;
watcher.on(EV.ERROR, async (error) => {
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
if (cont)
cont.watcherUnusable = true; // documented since Node 10.4.1
// Workaround for https://github.com/joyent/node/issues/4337
if (isWindows && error.code === 'EPERM') {
try {
const fd = await open(path, 'r');
await fd.close();
broadcastErr(error);
}
catch (err) {
// do nothing
}
}
else {
broadcastErr(error);
}
});
cont = {
listeners: listener,
errHandlers: errHandler,
rawEmitters: rawEmitter,
watcher,
};
FsWatchInstances.set(fullPath, cont);
}
// const index = cont.listeners.indexOf(listener);
// removes this instance's listeners and closes the underlying fs_watch
// instance if there are no more listeners left
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_ERR, errHandler);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
// Check to protect against issue gh-730.
// if (cont.watcherUnusable) {
cont.watcher.close();
// }
FsWatchInstances.delete(fullPath);
HANDLER_KEYS.forEach(clearItem(cont));
// @ts-ignore
cont.watcher = undefined;
Object.freeze(cont);
}
};
};
// fs_watchFile helpers
// object to hold per-process fs_watchFile instances
// (may be shared across chokidar FSWatcher instances)
const FsWatchFileInstances = new Map();
/**
* Instantiates the fs_watchFile interface or binds listeners
* to an existing one covering the same file system entry
* @param path to be watched
* @param fullPath absolute path
* @param options options to be passed to fs_watchFile
* @param handlers container for event listener functions
* @returns closer
*/
const setFsWatchFileListener = (path, fullPath, options, handlers) => {
const { listener, rawEmitter } = handlers;
let cont = FsWatchFileInstances.get(fullPath);
// let listeners = new Set();
// let rawEmitters = new Set();
const copts = cont && cont.options;
if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
// "Upgrade" the watcher to persistence or a quicker interval.
// This creates some unlikely edge case issues if the user mixes
// settings in a very weird way, but solving for those cases
// doesn't seem worthwhile for the added complexity.
// listeners = cont.listeners;
// rawEmitters = cont.rawEmitters;
unwatchFile(fullPath);
cont = undefined;
}
if (cont) {
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_RAW, rawEmitter);
}
else {
// TODO
// listeners.add(listener);
// rawEmitters.add(rawEmitter);
cont = {
listeners: listener,
rawEmitters: rawEmitter,
options,
watcher: watchFile(fullPath, options, (curr, prev) => {
foreach(cont.rawEmitters, (rawEmitter) => {
rawEmitter(EV.CHANGE, fullPath, { curr, prev });
});
const currmtime = curr.mtimeMs;
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
foreach(cont.listeners, (listener) => listener(path, curr));
}
}),
};
FsWatchFileInstances.set(fullPath, cont);
}
// const index = cont.listeners.indexOf(listener);
// Removes this instance's listeners and closes the underlying fs_watchFile
// instance if there are no more listeners left.
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
FsWatchFileInstances.delete(fullPath);
unwatchFile(fullPath);
cont.options = cont.watcher = undefined;
Object.freeze(cont);
}
};
};
/**
* @mixin
*/
export class NodeFsHandler {
constructor(fsW) {
this.fsw = fsW;
this._boundHandleError = (error) => fsW._handleError(error);
}
/**
* Watch file for changes with fs_watchFile or fs_watch.
* @param path to file or dir
* @param listener on fs change
* @returns closer for the watcher instance
*/
_watchWithNodeFs(path, listener) {
const opts = this.fsw.options;
const directory = sysPath.dirname(path);
const basename = sysPath.basename(path);
const parent = this.fsw._getWatchedDir(directory);
parent.add(basename);
const absolutePath = sysPath.resolve(path);
const options = {
persistent: opts.persistent,
};
if (!listener)
listener = EMPTY_FN;
let closer;
if (opts.usePolling) {
const enableBin = opts.interval !== opts.binaryInterval;
options.interval = enableBin && isBinaryPath(basename) ? opts.binaryInterval : opts.interval;
closer = setFsWatchFileListener(path, absolutePath, options, {
listener,
rawEmitter: this.fsw._emitRaw,
});
}
else {
closer = setFsWatchListener(path, absolutePath, options, {
listener,
errHandler: this._boundHandleError,
rawEmitter: this.fsw._emitRaw,
});
}
return closer;
}
/**
* Watch a file and emit add event if warranted.
* @returns closer for the watcher instance
*/
_handleFile(file, stats, initialAdd) {
if (this.fsw.closed) {
return;
}
const dirname = sysPath.dirname(file);
const basename = sysPath.basename(file);
const parent = this.fsw._getWatchedDir(dirname);
// stats is always present
let prevStats = stats;
// if the file is already being watched, do nothing
if (parent.has(basename))
return;
const listener = async (path, newStats) => {
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
return;
if (!newStats || newStats.mtimeMs === 0) {
try {
const newStats = await stat(file);
if (this.fsw.closed)
return;
// Check that change event was not fired because of changed only accessTime.
const at = newStats.atimeMs;
const mt = newStats.mtimeMs;
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit(EV.CHANGE, file, newStats);
}
if ((isMacos || isLinux) && prevStats.ino !== newStats.ino) {
this.fsw._closeFile(path);
prevStats = newStats;
const closer = this._watchWithNodeFs(file, listener);
if (closer)
this.fsw._addPathCloser(path, closer);
}
else {
prevStats = newStats;
}
}
catch (error) {
// Fix issues where mtime is null but file is still present
this.fsw._remove(dirname, basename);
}
// add is about to be emitted if file not already tracked in parent
}
else if (parent.has(basename)) {
// Check that change event was not fired because of changed only accessTime.
const at = newStats.atimeMs;
const mt = newStats.mtimeMs;
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit(EV.CHANGE, file, newStats);
}
prevStats = newStats;
}
};
// kick off the watcher
const closer = this._watchWithNodeFs(file, listener);
// emit an add event if we're supposed to
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
if (!this.fsw._throttle(EV.ADD, file, 0))
return;
this.fsw._emit(EV.ADD, file, stats);
}
return closer;
}
/**
* Handle symlinks encountered while reading a dir.
* @param entry returned by readdirp
* @param directory path of dir being read
* @param path of this item
* @param item basename of this item
* @returns true if no more processing is needed for this entry.
*/
async _handleSymlink(entry, directory, path, item) {
if (this.fsw.closed) {
return;
}
const full = entry.fullPath;
const dir = this.fsw._getWatchedDir(directory);
if (!this.fsw.options.followSymlinks) {
// watch symlink directly (don't follow) and detect changes
this.fsw._incrReadyCount();
let linkPath;
try {
linkPath = await fsrealpath(path);
}
catch (e) {
this.fsw._emitReady();
return true;
}
if (this.fsw.closed)
return;
if (dir.has(item)) {
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit(EV.CHANGE, path, entry.stats);
}
}
else {
dir.add(item);
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit(EV.ADD, path, entry.stats);
}
this.fsw._emitReady();
return true;
}
// don't follow the same symlink more than once
if (this.fsw._symlinkPaths.has(full)) {
return true;
}
this.fsw._symlinkPaths.set(full, true);
}
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
// Normalize the directory name on Windows
directory = sysPath.join(directory, '');
throttler = this.fsw._throttle('readdir', directory, 1000);
if (!throttler)
return;
const previous = this.fsw._getWatchedDir(wh.path);
const current = new Set();
let stream = this.fsw._readdirp(directory, {
fileFilter: (entry) => wh.filterPath(entry),
directoryFilter: (entry) => wh.filterDir(entry),
});
if (!stream)
return;
stream
.on(STR_DATA, async (entry) => {
if (this.fsw.closed) {
stream = undefined;
return;
}
const item = entry.path;
let path = sysPath.join(directory, item);
current.add(item);
if (entry.stats.isSymbolicLink() &&
(await this._handleSymlink(entry, directory, path, item))) {
return;
}
if (this.fsw.closed) {
stream = undefined;
return;
}
// Files that present in current directory snapshot
// but absent in previous are added to watch list and
// emit `add` event.
if (item === target || (!target && !previous.has(item))) {
this.fsw._incrReadyCount();
// ensure relativeness of path is preserved in case of watcher reuse
path = sysPath.join(dir, sysPath.relative(dir, path));
this._addToNodeFs(path, initialAdd, wh, depth + 1);
}
})
.on(EV.ERROR, this._boundHandleError);
return new Promise((resolve, reject) => {
if (!stream)
return reject();
stream.once(STR_END, () => {
if (this.fsw.closed) {
stream = undefined;
return;
}
const wasThrottled = throttler ? throttler.clear() : false;
resolve(undefined);
// Files that absent in current directory snapshot
// but present in previous emit `remove` event
// and are removed from @watched[directory].
previous
.getChildren()
.filter((item) => {
return item !== directory && !current.has(item);
})
.forEach((item) => {
this.fsw._remove(directory, item);
});
stream = undefined;
// one more time for any missed in case changes came in extremely quickly
if (wasThrottled)
this._handleRead(directory, false, wh, target, dir, depth, throttler);
});
});
}
/**
* Read directory to add / remove files from `@watched` list and re-read it on change.
* @param dir fs path
* @param stats
* @param initialAdd
* @param depth relative to user-supplied path
* @param target child path targeted for watch
* @param wh Common watch helpers for this path
* @param realpath
* @returns closer for the watcher instance.
*/
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {
const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
const tracked = parentDir.has(sysPath.basename(dir));
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
this.fsw._emit(EV.ADD_DIR, dir, stats);
}
// ensure dir is tracked (harmless if redundant)
parentDir.add(sysPath.basename(dir));
this.fsw._getWatchedDir(dir);
let throttler;
let closer;
const oDepth = this.fsw.options.depth;
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) {
if (!target) {
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
if (this.fsw.closed)
return;
}
closer = this._watchWithNodeFs(dir, (dirPath, stats) => {
// if current directory is removed, do nothing
if (stats && stats.mtimeMs === 0)
return;
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
});
}
return closer;
}
/**
* Handle added file, directory, or glob pattern.
* Delegates call to _handleFile / _handleDir after checks.
* @param path to file or ir
* @param initialAdd was the file added at watch instantiation?
* @param priorWh depth relative to user-supplied path
* @param depth Child path actually targeted for watch
* @param target Child path actually targeted for watch
*/
async _addToNodeFs(path, initialAdd, priorWh, depth, target) {
const ready = this.fsw._emitReady;
if (this.fsw._isIgnored(path) || this.fsw.closed) {
ready();
return false;
}
const wh = this.fsw._getWatchHelpers(path);
if (priorWh) {
wh.filterPath = (entry) => priorWh.filterPath(entry);
wh.filterDir = (entry) => priorWh.filterDir(entry);
}
// evaluate what is at the path we're being asked to watch
try {
const stats = await statMethods[wh.statMethod](wh.watchPath);
if (this.fsw.closed)
return;
if (this.fsw._isIgnored(wh.watchPath, stats)) {
ready();
return false;
}
const follow = this.fsw.options.followSymlinks;
let closer;
if (stats.isDirectory()) {
const absPath = sysPath.resolve(path);
const targetPath = follow ? await fsrealpath(path) : path;
if (this.fsw.closed)
return;
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
if (this.fsw.closed)
return;
// preserve this symlink's target path
if (absPath !== targetPath && targetPath !== undefined) {
this.fsw._symlinkPaths.set(absPath, targetPath);
}
}
else if (stats.isSymbolicLink()) {
const targetPath = follow ? await fsrealpath(path) : path;
if (this.fsw.closed)
return;
const parent = sysPath.dirname(wh.watchPath);
this.fsw._getWatchedDir(parent).add(wh.watchPath);
this.fsw._emit(EV.ADD, wh.watchPath, stats);
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
if (this.fsw.closed)
return;
// preserve this symlink's target path
if (targetPath !== undefined) {
this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
}
}
else {
closer = this._handleFile(wh.watchPath, stats, initialAdd);
}
ready();
if (closer)
this.fsw._addPathCloser(path, closer);
return false;
}
catch (error) {
if (this.fsw._handleError(error)) {
ready();
return path;
}
}
}
}
//# sourceMappingURL=handler.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,204 @@
import { Stats } from 'fs';
import { EventEmitter } from 'events';
import { ReaddirpStream, ReaddirpOptions, EntryInfo } from 'readdirp';
import { NodeFsHandler, EventName, Path } from './handler.js';
type AWF = {
stabilityThreshold: number;
pollInterval: number;
};
type BasicOpts = {
persistent: boolean;
ignoreInitial: boolean;
followSymlinks: boolean;
cwd?: string;
usePolling: boolean;
interval: number;
binaryInterval: number;
alwaysStat?: boolean;
depth?: number;
ignorePermissionErrors: boolean;
atomic: boolean | number;
};
export type Throttler = {
timeoutObject: NodeJS.Timeout;
clear: () => void;
count: number;
};
export type ChokidarOptions = Partial<BasicOpts & {
ignored: Matcher | Matcher[];
awaitWriteFinish: boolean | Partial<AWF>;
}>;
export type FSWInstanceOptions = BasicOpts & {
ignored: Matcher[];
awaitWriteFinish: false | AWF;
};
export type ThrottleType = 'readdir' | 'watch' | 'add' | 'remove' | 'change';
export type EmitArgs = [EventName, Path | Error, any?, any?, any?];
export type MatchFunction = (val: string, stats?: Stats) => boolean;
export interface MatcherObject {
path: string;
recursive?: boolean;
}
export type Matcher = string | RegExp | MatchFunction | MatcherObject;
/**
* Directory entry.
*/
declare class DirEntry {
path: Path;
_removeWatcher: (dir: string, base: string) => void;
items: Set<Path>;
constructor(dir: Path, removeWatcher: (dir: string, base: string) => void);
add(item: string): void;
remove(item: string): Promise<void>;
has(item: string): boolean | undefined;
getChildren(): string[];
dispose(): void;
}
export declare class WatchHelper {
fsw: FSWatcher;
path: string;
watchPath: string;
fullWatchPath: string;
dirParts: string[][];
followSymlinks: boolean;
statMethod: 'stat' | 'lstat';
constructor(path: string, follow: boolean, fsw: FSWatcher);
entryPath(entry: EntryInfo): Path;
filterPath(entry: EntryInfo): boolean;
filterDir(entry: EntryInfo): boolean;
}
/**
* Watches files & directories for changes. Emitted events:
* `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
*
* new FSWatcher()
* .add(directories)
* .on('add', path => log('File', path, 'was added'))
*/
export declare class FSWatcher extends EventEmitter {
closed: boolean;
options: FSWInstanceOptions;
_closers: Map<string, Array<any>>;
_ignoredPaths: Set<Matcher>;
_throttled: Map<ThrottleType, Map<any, any>>;
_streams: Set<ReaddirpStream>;
_symlinkPaths: Map<Path, string | boolean>;
_watched: Map<string, DirEntry>;
_pendingWrites: Map<string, any>;
_pendingUnlinks: Map<string, EmitArgs>;
_readyCount: number;
_emitReady: () => void;
_closePromise?: Promise<void>;
_userIgnored?: MatchFunction;
_readyEmitted: boolean;
_emitRaw: () => void;
_boundRemove: (dir: string, item: string) => void;
_nodeFsHandler: NodeFsHandler;
constructor(_opts?: ChokidarOptions);
_addIgnoredPath(matcher: Matcher): void;
_removeIgnoredPath(matcher: Matcher): void;
/**
* Adds paths to be watched on an existing FSWatcher instance.
* @param paths_ file or file list. Other arguments are unused
*/
add(paths_: Path | Path[], _origAdd?: string, _internal?: boolean): FSWatcher;
/**
* Close watchers or start ignoring events from specified paths.
*/
unwatch(paths_: Path | Path[]): FSWatcher;
/**
* Close watchers and remove all listeners from watched paths.
*/
close(): Promise<void>;
/**
* Expose list of watched paths
* @returns for chaining
*/
getWatched(): Record<string, string[]>;
emitWithAll(event: EventName, args: EmitArgs): void;
/**
* Normalize and emit events.
* Calling _emit DOES NOT MEAN emit() would be called!
* @param event Type of event
* @param path File or directory path
* @param stats arguments to be passed with event
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
_emit(event: EventName, path: Path, stats?: Stats): Promise<this | undefined>;
/**
* Common handler for errors
* @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
_handleError(error: Error): Error | boolean;
/**
* Helper utility for throttling
* @param actionType type being throttled
* @param path being acted upon
* @param timeout duration of time to suppress duplicate actions
* @returns tracking object or false if action should be suppressed
*/
_throttle(actionType: ThrottleType, path: Path, timeout: number): Throttler | false;
_incrReadyCount(): number;
/**
* Awaits write operation to finish.
* Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
* @param path being acted upon
* @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
* @param event
* @param awfEmit Callback to be called when ready for event to be emitted.
*/
_awaitWriteFinish(path: Path, threshold: number, event: EventName, awfEmit: (err?: Error, stat?: Stats) => void): void;
/**
* Determines whether user has asked to ignore this path.
*/
_isIgnored(path: Path, stats?: Stats): boolean;
_isntIgnored(path: Path, stat?: Stats): boolean;
/**
* Provides a set of common helpers and properties relating to symlink handling.
* @param path file or directory pattern being watched
*/
_getWatchHelpers(path: Path): WatchHelper;
/**
* Provides directory tracking objects
* @param directory path of the directory
*/
_getWatchedDir(directory: string): DirEntry;
/**
* Check for read permissions: https://stackoverflow.com/a/11781404/1358405
*/
_hasReadPermissions(stats: Stats): boolean;
/**
* Handles emitting unlink events for
* files and directories, and via recursion, for
* files and directories within directories that are unlinked
* @param directory within which the following item is located
* @param item base path of item/directory
*/
_remove(directory: string, item: string, isDirectory?: boolean): void;
/**
* Closes all watchers for a path
*/
_closePath(path: Path): void;
/**
* Closes only file-specific watchers
*/
_closeFile(path: Path): void;
_addPathCloser(path: Path, closer: () => void): void;
_readdirp(root: Path, opts?: Partial<ReaddirpOptions>): ReaddirpStream | undefined;
}
/**
* Instantiates watcher with paths to be tracked.
* @param paths file / directory paths
* @param options opts, such as `atomic`, `awaitWriteFinish`, `ignored`, and others
* @returns an instance of FSWatcher for chaining.
* @example
* const watcher = watch('.').on('all', (event, path) => { console.log(event, path); });
* watch('.', { atomic: true, awaitWriteFinish: true, ignored: (f, stats) => stats?.isFile() && !f.endsWith('.js') })
*/
export declare function watch(paths: string | string[], options?: ChokidarOptions): FSWatcher;
declare const _default: {
watch: typeof watch;
FSWatcher: typeof FSWatcher;
};
export default _default;
//# sourceMappingURL=index.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,798 @@
import { stat as statcb } from 'fs';
import { stat, readdir } from 'fs/promises';
import { EventEmitter } from 'events';
import * as sysPath from 'path';
import { readdirp } from 'readdirp';
import { NodeFsHandler, EVENTS as EV, isWindows, isIBMi, EMPTY_FN, STR_CLOSE, STR_END, } from './handler.js';
const SLASH = '/';
const SLASH_SLASH = '//';
const ONE_DOT = '.';
const TWO_DOTS = '..';
const STRING_TYPE = 'string';
const BACK_SLASH_RE = /\\/g;
const DOUBLE_SLASH_RE = /\/\//;
const DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
const REPLACER_RE = /^\.[/\\]/;
function arrify(item) {
return Array.isArray(item) ? item : [item];
}
const isMatcherObject = (matcher) => typeof matcher === 'object' && matcher !== null && !(matcher instanceof RegExp);
function createPattern(matcher) {
if (typeof matcher === 'function')
return matcher;
if (typeof matcher === 'string')
return (string) => matcher === string;
if (matcher instanceof RegExp)
return (string) => matcher.test(string);
if (typeof matcher === 'object' && matcher !== null) {
return (string) => {
if (matcher.path === string)
return true;
if (matcher.recursive) {
const relative = sysPath.relative(matcher.path, string);
if (!relative) {
return false;
}
return !relative.startsWith('..') && !sysPath.isAbsolute(relative);
}
return false;
};
}
return () => false;
}
function normalizePath(path) {
if (typeof path !== 'string')
throw new Error('string expected');
path = sysPath.normalize(path);
path = path.replace(/\\/g, '/');
let prepend = false;
if (path.startsWith('//'))
prepend = true;
const DOUBLE_SLASH_RE = /\/\//;
while (path.match(DOUBLE_SLASH_RE))
path = path.replace(DOUBLE_SLASH_RE, '/');
if (prepend)
path = '/' + path;
return path;
}
function matchPatterns(patterns, testString, stats) {
const path = normalizePath(testString);
for (let index = 0; index < patterns.length; index++) {
const pattern = patterns[index];
if (pattern(path, stats)) {
return true;
}
}
return false;
}
function anymatch(matchers, testString) {
if (matchers == null) {
throw new TypeError('anymatch: specify first argument');
}
// Early cache for matchers.
const matchersArray = arrify(matchers);
const patterns = matchersArray.map((matcher) => createPattern(matcher));
if (testString == null) {
return (testString, stats) => {
return matchPatterns(patterns, testString, stats);
};
}
return matchPatterns(patterns, testString);
}
const unifyPaths = (paths_) => {
const paths = arrify(paths_).flat();
if (!paths.every((p) => typeof p === STRING_TYPE)) {
throw new TypeError(`Non-string provided as watch path: ${paths}`);
}
return paths.map(normalizePathToUnix);
};
// If SLASH_SLASH occurs at the beginning of path, it is not replaced
// because "//StoragePC/DrivePool/Movies" is a valid network path
const toUnix = (string) => {
let str = string.replace(BACK_SLASH_RE, SLASH);
let prepend = false;
if (str.startsWith(SLASH_SLASH)) {
prepend = true;
}
while (str.match(DOUBLE_SLASH_RE)) {
str = str.replace(DOUBLE_SLASH_RE, SLASH);
}
if (prepend) {
str = SLASH + str;
}
return str;
};
// Our version of upath.normalize
// TODO: this is not equal to path-normalize module - investigate why
const normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));
// TODO: refactor
const normalizeIgnored = (cwd = '') => (path) => {
if (typeof path === 'string') {
return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));
}
else {
return path;
}
};
const getAbsolutePath = (path, cwd) => {
if (sysPath.isAbsolute(path)) {
return path;
}
return sysPath.join(cwd, path);
};
const EMPTY_SET = Object.freeze(new Set());
/**
* Directory entry.
*/
class DirEntry {
constructor(dir, removeWatcher) {
this.path = dir;
this._removeWatcher = removeWatcher;
this.items = new Set();
}
add(item) {
const { items } = this;
if (!items)
return;
if (item !== ONE_DOT && item !== TWO_DOTS)
items.add(item);
}
async remove(item) {
const { items } = this;
if (!items)
return;
items.delete(item);
if (items.size > 0)
return;
const dir = this.path;
try {
await readdir(dir);
}
catch (err) {
if (this._removeWatcher) {
this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));
}
}
}
has(item) {
const { items } = this;
if (!items)
return;
return items.has(item);
}
getChildren() {
const { items } = this;
if (!items)
return [];
return [...items.values()];
}
dispose() {
this.items.clear();
this.path = '';
this._removeWatcher = EMPTY_FN;
this.items = EMPTY_SET;
Object.freeze(this);
}
}
const STAT_METHOD_F = 'stat';
const STAT_METHOD_L = 'lstat';
export class WatchHelper {
constructor(path, follow, fsw) {
this.fsw = fsw;
const watchPath = path;
this.path = path = path.replace(REPLACER_RE, '');
this.watchPath = watchPath;
this.fullWatchPath = sysPath.resolve(watchPath);
this.dirParts = [];
this.dirParts.forEach((parts) => {
if (parts.length > 1)
parts.pop();
});
this.followSymlinks = follow;
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
}
entryPath(entry) {
return sysPath.join(this.watchPath, sysPath.relative(this.watchPath, entry.fullPath));
}
filterPath(entry) {
const { stats } = entry;
if (stats && stats.isSymbolicLink())
return this.filterDir(entry);
const resolvedPath = this.entryPath(entry);
// TODO: what if stats is undefined? remove !
return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);
}
filterDir(entry) {
return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
}
}
/**
* Watches files & directories for changes. Emitted events:
* `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
*
* new FSWatcher()
* .add(directories)
* .on('add', path => log('File', path, 'was added'))
*/
export class FSWatcher extends EventEmitter {
// Not indenting methods for history sake; for now.
constructor(_opts = {}) {
super();
this.closed = false;
this._closers = new Map();
this._ignoredPaths = new Set();
this._throttled = new Map();
this._streams = new Set();
this._symlinkPaths = new Map();
this._watched = new Map();
this._pendingWrites = new Map();
this._pendingUnlinks = new Map();
this._readyCount = 0;
this._readyEmitted = false;
const awf = _opts.awaitWriteFinish;
const DEF_AWF = { stabilityThreshold: 2000, pollInterval: 100 };
const opts = {
// Defaults
persistent: true,
ignoreInitial: false,
ignorePermissionErrors: false,
interval: 100,
binaryInterval: 300,
followSymlinks: true,
usePolling: false,
// useAsync: false,
atomic: true, // NOTE: overwritten later (depends on usePolling)
..._opts,
// Change format
ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),
awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === 'object' ? { ...DEF_AWF, ...awf } : false,
};
// Always default to polling on IBM i because fs.watch() is not available on IBM i.
if (isIBMi)
opts.usePolling = true;
// Editor atomic write normalization enabled by default with fs.watch
if (opts.atomic === undefined)
opts.atomic = !opts.usePolling;
// opts.atomic = typeof _opts.atomic === 'number' ? _opts.atomic : 100;
// Global override. Useful for developers, who need to force polling for all
// instances of chokidar, regardless of usage / dependency depth
const envPoll = process.env.CHOKIDAR_USEPOLLING;
if (envPoll !== undefined) {
const envLower = envPoll.toLowerCase();
if (envLower === 'false' || envLower === '0')
opts.usePolling = false;
else if (envLower === 'true' || envLower === '1')
opts.usePolling = true;
else
opts.usePolling = !!envLower;
}
const envInterval = process.env.CHOKIDAR_INTERVAL;
if (envInterval)
opts.interval = Number.parseInt(envInterval, 10);
// This is done to emit ready only once, but each 'add' will increase that?
let readyCalls = 0;
this._emitReady = () => {
readyCalls++;
if (readyCalls >= this._readyCount) {
this._emitReady = EMPTY_FN;
this._readyEmitted = true;
// use process.nextTick to allow time for listener to be bound
process.nextTick(() => this.emit(EV.READY));
}
};
this._emitRaw = (...args) => this.emit(EV.RAW, ...args);
this._boundRemove = this._remove.bind(this);
this.options = opts;
this._nodeFsHandler = new NodeFsHandler(this);
// Youre frozen when your hearts not open.
Object.freeze(opts);
}
_addIgnoredPath(matcher) {
if (isMatcherObject(matcher)) {
// return early if we already have a deeply equal matcher object
for (const ignored of this._ignoredPaths) {
if (isMatcherObject(ignored) &&
ignored.path === matcher.path &&
ignored.recursive === matcher.recursive) {
return;
}
}
}
this._ignoredPaths.add(matcher);
}
_removeIgnoredPath(matcher) {
this._ignoredPaths.delete(matcher);
// now find any matcher objects with the matcher as path
if (typeof matcher === 'string') {
for (const ignored of this._ignoredPaths) {
// TODO (43081j): make this more efficient.
// probably just make a `this._ignoredDirectories` or some
// such thing.
if (isMatcherObject(ignored) && ignored.path === matcher) {
this._ignoredPaths.delete(ignored);
}
}
}
}
// Public methods
/**
* Adds paths to be watched on an existing FSWatcher instance.
* @param paths_ file or file list. Other arguments are unused
*/
add(paths_, _origAdd, _internal) {
const { cwd } = this.options;
this.closed = false;
this._closePromise = undefined;
let paths = unifyPaths(paths_);
if (cwd) {
paths = paths.map((path) => {
const absPath = getAbsolutePath(path, cwd);
// Check `path` instead of `absPath` because the cwd portion can't be a glob
return absPath;
});
}
paths.forEach((path) => {
this._removeIgnoredPath(path);
});
this._userIgnored = undefined;
if (!this._readyCount)
this._readyCount = 0;
this._readyCount += paths.length;
Promise.all(paths.map(async (path) => {
const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, undefined, 0, _origAdd);
if (res)
this._emitReady();
return res;
})).then((results) => {
if (this.closed)
return;
results.forEach((item) => {
if (item)
this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
});
});
return this;
}
/**
* Close watchers or start ignoring events from specified paths.
*/
unwatch(paths_) {
if (this.closed)
return this;
const paths = unifyPaths(paths_);
const { cwd } = this.options;
paths.forEach((path) => {
// convert to absolute path unless relative path already matches
if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {
if (cwd)
path = sysPath.join(cwd, path);
path = sysPath.resolve(path);
}
this._closePath(path);
this._addIgnoredPath(path);
if (this._watched.has(path)) {
this._addIgnoredPath({
path,
recursive: true,
});
}
// reset the cached userIgnored anymatch fn
// to make ignoredPaths changes effective
this._userIgnored = undefined;
});
return this;
}
/**
* Close watchers and remove all listeners from watched paths.
*/
close() {
if (this._closePromise) {
return this._closePromise;
}
this.closed = true;
// Memory management.
this.removeAllListeners();
const closers = [];
this._closers.forEach((closerList) => closerList.forEach((closer) => {
const promise = closer();
if (promise instanceof Promise)
closers.push(promise);
}));
this._streams.forEach((stream) => stream.destroy());
this._userIgnored = undefined;
this._readyCount = 0;
this._readyEmitted = false;
this._watched.forEach((dirent) => dirent.dispose());
this._closers.clear();
this._watched.clear();
this._streams.clear();
this._symlinkPaths.clear();
this._throttled.clear();
this._closePromise = closers.length
? Promise.all(closers).then(() => undefined)
: Promise.resolve();
return this._closePromise;
}
/**
* Expose list of watched paths
* @returns for chaining
*/
getWatched() {
const watchList = {};
this._watched.forEach((entry, dir) => {
const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;
const index = key || ONE_DOT;
watchList[index] = entry.getChildren().sort();
});
return watchList;
}
emitWithAll(event, args) {
this.emit(...args);
if (event !== EV.ERROR)
this.emit(EV.ALL, ...args);
}
// Common helpers
// --------------
/**
* Normalize and emit events.
* Calling _emit DOES NOT MEAN emit() would be called!
* @param event Type of event
* @param path File or directory path
* @param stats arguments to be passed with event
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
async _emit(event, path, stats) {
if (this.closed)
return;
const opts = this.options;
if (isWindows)
path = sysPath.normalize(path);
if (opts.cwd)
path = sysPath.relative(opts.cwd, path);
const args = [event, path];
if (stats != null)
args.push(stats);
const awf = opts.awaitWriteFinish;
let pw;
if (awf && (pw = this._pendingWrites.get(path))) {
pw.lastChange = new Date();
return this;
}
if (opts.atomic) {
if (event === EV.UNLINK) {
this._pendingUnlinks.set(path, args);
setTimeout(() => {
this._pendingUnlinks.forEach((entry, path) => {
this.emit(...entry);
this.emit(EV.ALL, ...entry);
this._pendingUnlinks.delete(path);
});
}, typeof opts.atomic === 'number' ? opts.atomic : 100);
return this;
}
if (event === EV.ADD && this._pendingUnlinks.has(path)) {
event = args[0] = EV.CHANGE;
this._pendingUnlinks.delete(path);
}
}
if (awf && (event === EV.ADD || event === EV.CHANGE) && this._readyEmitted) {
const awfEmit = (err, stats) => {
if (err) {
event = args[0] = EV.ERROR;
args[1] = err;
this.emitWithAll(event, args);
}
else if (stats) {
// if stats doesn't exist the file must have been deleted
if (args.length > 2) {
args[2] = stats;
}
else {
args.push(stats);
}
this.emitWithAll(event, args);
}
};
this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
return this;
}
if (event === EV.CHANGE) {
const isThrottled = !this._throttle(EV.CHANGE, path, 50);
if (isThrottled)
return this;
}
if (opts.alwaysStat &&
stats === undefined &&
(event === EV.ADD || event === EV.ADD_DIR || event === EV.CHANGE)) {
const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;
let stats;
try {
stats = await stat(fullPath);
}
catch (err) {
// do nothing
}
// Suppress event when fs_stat fails, to avoid sending undefined 'stat'
if (!stats || this.closed)
return;
args.push(stats);
}
this.emitWithAll(event, args);
return this;
}
/**
* Common handler for errors
* @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
_handleError(error) {
const code = error && error.code;
if (error &&
code !== 'ENOENT' &&
code !== 'ENOTDIR' &&
(!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))) {
this.emit(EV.ERROR, error);
}
return error || this.closed;
}
/**
* Helper utility for throttling
* @param actionType type being throttled
* @param path being acted upon
* @param timeout duration of time to suppress duplicate actions
* @returns tracking object or false if action should be suppressed
*/
_throttle(actionType, path, timeout) {
if (!this._throttled.has(actionType)) {
this._throttled.set(actionType, new Map());
}
const action = this._throttled.get(actionType);
if (!action)
throw new Error('invalid throttle');
const actionPath = action.get(path);
if (actionPath) {
actionPath.count++;
return false;
}
// eslint-disable-next-line prefer-const
let timeoutObject;
const clear = () => {
const item = action.get(path);
const count = item ? item.count : 0;
action.delete(path);
clearTimeout(timeoutObject);
if (item)
clearTimeout(item.timeoutObject);
return count;
};
timeoutObject = setTimeout(clear, timeout);
const thr = { timeoutObject, clear, count: 0 };
action.set(path, thr);
return thr;
}
_incrReadyCount() {
return this._readyCount++;
}
/**
* Awaits write operation to finish.
* Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
* @param path being acted upon
* @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
* @param event
* @param awfEmit Callback to be called when ready for event to be emitted.
*/
_awaitWriteFinish(path, threshold, event, awfEmit) {
const awf = this.options.awaitWriteFinish;
if (typeof awf !== 'object')
return;
const pollInterval = awf.pollInterval;
let timeoutHandler;
let fullPath = path;
if (this.options.cwd && !sysPath.isAbsolute(path)) {
fullPath = sysPath.join(this.options.cwd, path);
}
const now = new Date();
const writes = this._pendingWrites;
function awaitWriteFinishFn(prevStat) {
statcb(fullPath, (err, curStat) => {
if (err || !writes.has(path)) {
if (err && err.code !== 'ENOENT')
awfEmit(err);
return;
}
const now = Number(new Date());
if (prevStat && curStat.size !== prevStat.size) {
writes.get(path).lastChange = now;
}
const pw = writes.get(path);
const df = now - pw.lastChange;
if (df >= threshold) {
writes.delete(path);
awfEmit(undefined, curStat);
}
else {
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
}
});
}
if (!writes.has(path)) {
writes.set(path, {
lastChange: now,
cancelWait: () => {
writes.delete(path);
clearTimeout(timeoutHandler);
return event;
},
});
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
}
}
/**
* Determines whether user has asked to ignore this path.
*/
_isIgnored(path, stats) {
if (this.options.atomic && DOT_RE.test(path))
return true;
if (!this._userIgnored) {
const { cwd } = this.options;
const ign = this.options.ignored;
const ignored = (ign || []).map(normalizeIgnored(cwd));
const ignoredPaths = [...this._ignoredPaths];
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
this._userIgnored = anymatch(list, undefined);
}
return this._userIgnored(path, stats);
}
_isntIgnored(path, stat) {
return !this._isIgnored(path, stat);
}
/**
* Provides a set of common helpers and properties relating to symlink handling.
* @param path file or directory pattern being watched
*/
_getWatchHelpers(path) {
return new WatchHelper(path, this.options.followSymlinks, this);
}
// Directory helpers
// -----------------
/**
* Provides directory tracking objects
* @param directory path of the directory
*/
_getWatchedDir(directory) {
const dir = sysPath.resolve(directory);
if (!this._watched.has(dir))
this._watched.set(dir, new DirEntry(dir, this._boundRemove));
return this._watched.get(dir);
}
// File helpers
// ------------
/**
* Check for read permissions: https://stackoverflow.com/a/11781404/1358405
*/
_hasReadPermissions(stats) {
if (this.options.ignorePermissionErrors)
return true;
return Boolean(Number(stats.mode) & 0o400);
}
/**
* Handles emitting unlink events for
* files and directories, and via recursion, for
* files and directories within directories that are unlinked
* @param directory within which the following item is located
* @param item base path of item/directory
*/
_remove(directory, item, isDirectory) {
// if what is being deleted is a directory, get that directory's paths
// for recursive deleting and cleaning of watched object
// if it is not a directory, nestedDirectoryChildren will be empty array
const path = sysPath.join(directory, item);
const fullPath = sysPath.resolve(path);
isDirectory =
isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);
// prevent duplicate handling in case of arriving here nearly simultaneously
// via multiple paths (such as _handleFile and _handleDir)
if (!this._throttle('remove', path, 100))
return;
// if the only watched file is removed, watch for its return
if (!isDirectory && this._watched.size === 1) {
this.add(directory, item, true);
}
// This will create a new entry in the watched object in either case
// so we got to do the directory check beforehand
const wp = this._getWatchedDir(path);
const nestedDirectoryChildren = wp.getChildren();
// Recursively remove children directories / files.
nestedDirectoryChildren.forEach((nested) => this._remove(path, nested));
// Check if item was on the watched list and remove it
const parent = this._getWatchedDir(directory);
const wasTracked = parent.has(item);
parent.remove(item);
// Fixes issue #1042 -> Relative paths were detected and added as symlinks
// (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),
// but never removed from the map in case the path was deleted.
// This leads to an incorrect state if the path was recreated:
// https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553
if (this._symlinkPaths.has(fullPath)) {
this._symlinkPaths.delete(fullPath);
}
// If we wait for this file to be fully written, cancel the wait.
let relPath = path;
if (this.options.cwd)
relPath = sysPath.relative(this.options.cwd, path);
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
const event = this._pendingWrites.get(relPath).cancelWait();
if (event === EV.ADD)
return;
}
// The Entry will either be a directory that just got removed
// or a bogus entry to a file, in either case we have to remove it
this._watched.delete(path);
this._watched.delete(fullPath);
const eventName = isDirectory ? EV.UNLINK_DIR : EV.UNLINK;
if (wasTracked && !this._isIgnored(path))
this._emit(eventName, path);
// Avoid conflicts if we later create another file with the same name
this._closePath(path);
}
/**
* Closes all watchers for a path
*/
_closePath(path) {
this._closeFile(path);
const dir = sysPath.dirname(path);
this._getWatchedDir(dir).remove(sysPath.basename(path));
}
/**
* Closes only file-specific watchers
*/
_closeFile(path) {
const closers = this._closers.get(path);
if (!closers)
return;
closers.forEach((closer) => closer());
this._closers.delete(path);
}
_addPathCloser(path, closer) {
if (!closer)
return;
let list = this._closers.get(path);
if (!list) {
list = [];
this._closers.set(path, list);
}
list.push(closer);
}
_readdirp(root, opts) {
if (this.closed)
return;
const options = { type: EV.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
let stream = readdirp(root, options);
this._streams.add(stream);
stream.once(STR_CLOSE, () => {
stream = undefined;
});
stream.once(STR_END, () => {
if (stream) {
this._streams.delete(stream);
stream = undefined;
}
});
return stream;
}
}
/**
* Instantiates watcher with paths to be tracked.
* @param paths file / directory paths
* @param options opts, such as `atomic`, `awaitWriteFinish`, `ignored`, and others
* @returns an instance of FSWatcher for chaining.
* @example
* const watcher = watch('.').on('all', (event, path) => { console.log(event, path); });
* watch('.', { atomic: true, awaitWriteFinish: true, ignored: (f, stats) => stats?.isFile() && !f.endsWith('.js') })
*/
export function watch(paths, options = {}) {
const watcher = new FSWatcher(options);
watcher.add(paths);
return watcher;
}
export default { watch, FSWatcher };
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{ "type": "module", "sideEffects": false }

View File

@ -0,0 +1,90 @@
import type { WatchEventType, Stats, FSWatcher as NativeFsWatcher } from 'fs';
import type { FSWatcher, WatchHelper, Throttler } from './index.js';
import type { EntryInfo } from 'readdirp';
export type Path = string;
export declare const STR_DATA = "data";
export declare const STR_END = "end";
export declare const STR_CLOSE = "close";
export declare const EMPTY_FN: () => void;
export declare const IDENTITY_FN: (val: unknown) => unknown;
export declare const isWindows: boolean;
export declare const isMacos: boolean;
export declare const isLinux: boolean;
export declare const isIBMi: boolean;
export declare const EVENTS: {
readonly ALL: "all";
readonly READY: "ready";
readonly ADD: "add";
readonly CHANGE: "change";
readonly ADD_DIR: "addDir";
readonly UNLINK: "unlink";
readonly UNLINK_DIR: "unlinkDir";
readonly RAW: "raw";
readonly ERROR: "error";
};
export type EventName = (typeof EVENTS)[keyof typeof EVENTS];
export type FsWatchContainer = {
listeners: (path: string) => void | Set<any>;
errHandlers: (err: unknown) => void | Set<any>;
rawEmitters: (ev: WatchEventType, path: string, opts: unknown) => void | Set<any>;
watcher: NativeFsWatcher;
watcherUnusable?: boolean;
};
export interface WatchHandlers {
listener: (path: string) => void;
errHandler: (err: unknown) => void;
rawEmitter: (ev: WatchEventType, path: string, opts: unknown) => void;
}
/**
* @mixin
*/
export declare class NodeFsHandler {
fsw: FSWatcher;
_boundHandleError: (error: unknown) => void;
constructor(fsW: FSWatcher);
/**
* Watch file for changes with fs_watchFile or fs_watch.
* @param path to file or dir
* @param listener on fs change
* @returns closer for the watcher instance
*/
_watchWithNodeFs(path: string, listener: (path: string, newStats?: any) => void | Promise<void>): (() => void) | undefined;
/**
* Watch a file and emit add event if warranted.
* @returns closer for the watcher instance
*/
_handleFile(file: Path, stats: Stats, initialAdd: boolean): (() => void) | undefined;
/**
* Handle symlinks encountered while reading a dir.
* @param entry returned by readdirp
* @param directory path of dir being read
* @param path of this item
* @param item basename of this item
* @returns true if no more processing is needed for this entry.
*/
_handleSymlink(entry: EntryInfo, directory: string, path: Path, item: string): Promise<boolean | undefined>;
_handleRead(directory: string, initialAdd: boolean, wh: WatchHelper, target: Path, dir: Path, depth: number, throttler: Throttler): Promise<unknown> | undefined;
/**
* Read directory to add / remove files from `@watched` list and re-read it on change.
* @param dir fs path
* @param stats
* @param initialAdd
* @param depth relative to user-supplied path
* @param target child path targeted for watch
* @param wh Common watch helpers for this path
* @param realpath
* @returns closer for the watcher instance.
*/
_handleDir(dir: string, stats: Stats, initialAdd: boolean, depth: number, target: string, wh: WatchHelper, realpath: string): Promise<(() => void) | undefined>;
/**
* Handle added file, directory, or glob pattern.
* Delegates call to _handleFile / _handleDir after checks.
* @param path to file or ir
* @param initialAdd was the file added at watch instantiation?
* @param priorWh depth relative to user-supplied path
* @param depth Child path actually targeted for watch
* @param target Child path actually targeted for watch
*/
_addToNodeFs(path: string, initialAdd: boolean, priorWh: WatchHelper | undefined, depth: number, target?: string): Promise<string | false | undefined>;
}
//# sourceMappingURL=handler.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["src/handler.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,635 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeFsHandler = exports.EVENTS = exports.isIBMi = exports.isLinux = exports.isMacos = exports.isWindows = exports.IDENTITY_FN = exports.EMPTY_FN = exports.STR_CLOSE = exports.STR_END = exports.STR_DATA = void 0;
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const sysPath = require("path");
const os_1 = require("os");
exports.STR_DATA = 'data';
exports.STR_END = 'end';
exports.STR_CLOSE = 'close';
const EMPTY_FN = () => { };
exports.EMPTY_FN = EMPTY_FN;
const IDENTITY_FN = (val) => val;
exports.IDENTITY_FN = IDENTITY_FN;
const pl = process.platform;
exports.isWindows = pl === 'win32';
exports.isMacos = pl === 'darwin';
exports.isLinux = pl === 'linux';
exports.isIBMi = (0, os_1.type)() === 'OS400';
exports.EVENTS = {
ALL: 'all',
READY: 'ready',
ADD: 'add',
CHANGE: 'change',
ADD_DIR: 'addDir',
UNLINK: 'unlink',
UNLINK_DIR: 'unlinkDir',
RAW: 'raw',
ERROR: 'error',
};
const EV = exports.EVENTS;
const THROTTLE_MODE_WATCH = 'watch';
const statMethods = { lstat: promises_1.lstat, stat: promises_1.stat };
const KEY_LISTENERS = 'listeners';
const KEY_ERR = 'errHandlers';
const KEY_RAW = 'rawEmitters';
const HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
// prettier-ignore
const binaryExtensions = new Set([
'3dm', '3ds', '3g2', '3gp', '7z', 'a', 'aac', 'adp', 'afdesign', 'afphoto', 'afpub', 'ai',
'aif', 'aiff', 'alz', 'ape', 'apk', 'appimage', 'ar', 'arj', 'asf', 'au', 'avi',
'bak', 'baml', 'bh', 'bin', 'bk', 'bmp', 'btif', 'bz2', 'bzip2',
'cab', 'caf', 'cgm', 'class', 'cmx', 'cpio', 'cr2', 'cur', 'dat', 'dcm', 'deb', 'dex', 'djvu',
'dll', 'dmg', 'dng', 'doc', 'docm', 'docx', 'dot', 'dotm', 'dra', 'DS_Store', 'dsk', 'dts',
'dtshd', 'dvb', 'dwg', 'dxf',
'ecelp4800', 'ecelp7470', 'ecelp9600', 'egg', 'eol', 'eot', 'epub', 'exe',
'f4v', 'fbs', 'fh', 'fla', 'flac', 'flatpak', 'fli', 'flv', 'fpx', 'fst', 'fvt',
'g3', 'gh', 'gif', 'graffle', 'gz', 'gzip',
'h261', 'h263', 'h264', 'icns', 'ico', 'ief', 'img', 'ipa', 'iso',
'jar', 'jpeg', 'jpg', 'jpgv', 'jpm', 'jxr', 'key', 'ktx',
'lha', 'lib', 'lvp', 'lz', 'lzh', 'lzma', 'lzo',
'm3u', 'm4a', 'm4v', 'mar', 'mdi', 'mht', 'mid', 'midi', 'mj2', 'mka', 'mkv', 'mmr', 'mng',
'mobi', 'mov', 'movie', 'mp3',
'mp4', 'mp4a', 'mpeg', 'mpg', 'mpga', 'mxu',
'nef', 'npx', 'numbers', 'nupkg',
'o', 'odp', 'ods', 'odt', 'oga', 'ogg', 'ogv', 'otf', 'ott',
'pages', 'pbm', 'pcx', 'pdb', 'pdf', 'pea', 'pgm', 'pic', 'png', 'pnm', 'pot', 'potm',
'potx', 'ppa', 'ppam',
'ppm', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx', 'psd', 'pya', 'pyc', 'pyo', 'pyv',
'qt',
'rar', 'ras', 'raw', 'resources', 'rgb', 'rip', 'rlc', 'rmf', 'rmvb', 'rpm', 'rtf', 'rz',
's3m', 's7z', 'scpt', 'sgi', 'shar', 'snap', 'sil', 'sketch', 'slk', 'smv', 'snk', 'so',
'stl', 'suo', 'sub', 'swf',
'tar', 'tbz', 'tbz2', 'tga', 'tgz', 'thmx', 'tif', 'tiff', 'tlz', 'ttc', 'ttf', 'txz',
'udf', 'uvh', 'uvi', 'uvm', 'uvp', 'uvs', 'uvu',
'viv', 'vob',
'war', 'wav', 'wax', 'wbmp', 'wdp', 'weba', 'webm', 'webp', 'whl', 'wim', 'wm', 'wma',
'wmv', 'wmx', 'woff', 'woff2', 'wrm', 'wvx',
'xbm', 'xif', 'xla', 'xlam', 'xls', 'xlsb', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx', 'xm',
'xmind', 'xpi', 'xpm', 'xwd', 'xz',
'z', 'zip', 'zipx',
]);
const isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
// TODO: emit errors properly. Example: EMFILE on Macos.
const foreach = (val, fn) => {
if (val instanceof Set) {
val.forEach(fn);
}
else {
fn(val);
}
};
const addAndConvert = (main, prop, item) => {
let container = main[prop];
if (!(container instanceof Set)) {
main[prop] = container = new Set([container]);
}
container.add(item);
};
const clearItem = (cont) => (key) => {
const set = cont[key];
if (set instanceof Set) {
set.clear();
}
else {
delete cont[key];
}
};
const delFromSet = (main, prop, item) => {
const container = main[prop];
if (container instanceof Set) {
container.delete(item);
}
else if (container === item) {
delete main[prop];
}
};
const isEmptySet = (val) => (val instanceof Set ? val.size === 0 : !val);
const FsWatchInstances = new Map();
/**
* Instantiates the fs_watch interface
* @param path to be watched
* @param options to be passed to fs_watch
* @param listener main event handler
* @param errHandler emits info about errors
* @param emitRaw emits raw event data
* @returns {NativeFsWatcher}
*/
function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
const handleEvent = (rawEvent, evPath) => {
listener(path);
emitRaw(rawEvent, evPath, { watchedPath: path });
// emit based on events occurring for files from a directory's watcher in
// case the file's watcher misses it (and rely on throttling to de-dupe)
if (evPath && path !== evPath) {
fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));
}
};
try {
return (0, fs_1.watch)(path, {
persistent: options.persistent,
}, handleEvent);
}
catch (error) {
errHandler(error);
return undefined;
}
}
/**
* Helper for passing fs_watch event data to a collection of listeners
* @param fullPath absolute path bound to fs_watch instance
*/
const fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
const cont = FsWatchInstances.get(fullPath);
if (!cont)
return;
foreach(cont[listenerType], (listener) => {
listener(val1, val2, val3);
});
};
/**
* Instantiates the fs_watch interface or binds listeners
* to an existing one covering the same file system entry
* @param path
* @param fullPath absolute path
* @param options to be passed to fs_watch
* @param handlers container for event listener functions
*/
const setFsWatchListener = (path, fullPath, options, handlers) => {
const { listener, errHandler, rawEmitter } = handlers;
let cont = FsWatchInstances.get(fullPath);
let watcher;
if (!options.persistent) {
watcher = createFsWatchInstance(path, options, listener, errHandler, rawEmitter);
if (!watcher)
return;
return watcher.close.bind(watcher);
}
if (cont) {
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_ERR, errHandler);
addAndConvert(cont, KEY_RAW, rawEmitter);
}
else {
watcher = createFsWatchInstance(path, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, // no need to use broadcast here
fsWatchBroadcast.bind(null, fullPath, KEY_RAW));
if (!watcher)
return;
watcher.on(EV.ERROR, async (error) => {
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
if (cont)
cont.watcherUnusable = true; // documented since Node 10.4.1
// Workaround for https://github.com/joyent/node/issues/4337
if (exports.isWindows && error.code === 'EPERM') {
try {
const fd = await (0, promises_1.open)(path, 'r');
await fd.close();
broadcastErr(error);
}
catch (err) {
// do nothing
}
}
else {
broadcastErr(error);
}
});
cont = {
listeners: listener,
errHandlers: errHandler,
rawEmitters: rawEmitter,
watcher,
};
FsWatchInstances.set(fullPath, cont);
}
// const index = cont.listeners.indexOf(listener);
// removes this instance's listeners and closes the underlying fs_watch
// instance if there are no more listeners left
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_ERR, errHandler);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
// Check to protect against issue gh-730.
// if (cont.watcherUnusable) {
cont.watcher.close();
// }
FsWatchInstances.delete(fullPath);
HANDLER_KEYS.forEach(clearItem(cont));
// @ts-ignore
cont.watcher = undefined;
Object.freeze(cont);
}
};
};
// fs_watchFile helpers
// object to hold per-process fs_watchFile instances
// (may be shared across chokidar FSWatcher instances)
const FsWatchFileInstances = new Map();
/**
* Instantiates the fs_watchFile interface or binds listeners
* to an existing one covering the same file system entry
* @param path to be watched
* @param fullPath absolute path
* @param options options to be passed to fs_watchFile
* @param handlers container for event listener functions
* @returns closer
*/
const setFsWatchFileListener = (path, fullPath, options, handlers) => {
const { listener, rawEmitter } = handlers;
let cont = FsWatchFileInstances.get(fullPath);
// let listeners = new Set();
// let rawEmitters = new Set();
const copts = cont && cont.options;
if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
// "Upgrade" the watcher to persistence or a quicker interval.
// This creates some unlikely edge case issues if the user mixes
// settings in a very weird way, but solving for those cases
// doesn't seem worthwhile for the added complexity.
// listeners = cont.listeners;
// rawEmitters = cont.rawEmitters;
(0, fs_1.unwatchFile)(fullPath);
cont = undefined;
}
if (cont) {
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_RAW, rawEmitter);
}
else {
// TODO
// listeners.add(listener);
// rawEmitters.add(rawEmitter);
cont = {
listeners: listener,
rawEmitters: rawEmitter,
options,
watcher: (0, fs_1.watchFile)(fullPath, options, (curr, prev) => {
foreach(cont.rawEmitters, (rawEmitter) => {
rawEmitter(EV.CHANGE, fullPath, { curr, prev });
});
const currmtime = curr.mtimeMs;
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
foreach(cont.listeners, (listener) => listener(path, curr));
}
}),
};
FsWatchFileInstances.set(fullPath, cont);
}
// const index = cont.listeners.indexOf(listener);
// Removes this instance's listeners and closes the underlying fs_watchFile
// instance if there are no more listeners left.
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
FsWatchFileInstances.delete(fullPath);
(0, fs_1.unwatchFile)(fullPath);
cont.options = cont.watcher = undefined;
Object.freeze(cont);
}
};
};
/**
* @mixin
*/
class NodeFsHandler {
constructor(fsW) {
this.fsw = fsW;
this._boundHandleError = (error) => fsW._handleError(error);
}
/**
* Watch file for changes with fs_watchFile or fs_watch.
* @param path to file or dir
* @param listener on fs change
* @returns closer for the watcher instance
*/
_watchWithNodeFs(path, listener) {
const opts = this.fsw.options;
const directory = sysPath.dirname(path);
const basename = sysPath.basename(path);
const parent = this.fsw._getWatchedDir(directory);
parent.add(basename);
const absolutePath = sysPath.resolve(path);
const options = {
persistent: opts.persistent,
};
if (!listener)
listener = exports.EMPTY_FN;
let closer;
if (opts.usePolling) {
const enableBin = opts.interval !== opts.binaryInterval;
options.interval = enableBin && isBinaryPath(basename) ? opts.binaryInterval : opts.interval;
closer = setFsWatchFileListener(path, absolutePath, options, {
listener,
rawEmitter: this.fsw._emitRaw,
});
}
else {
closer = setFsWatchListener(path, absolutePath, options, {
listener,
errHandler: this._boundHandleError,
rawEmitter: this.fsw._emitRaw,
});
}
return closer;
}
/**
* Watch a file and emit add event if warranted.
* @returns closer for the watcher instance
*/
_handleFile(file, stats, initialAdd) {
if (this.fsw.closed) {
return;
}
const dirname = sysPath.dirname(file);
const basename = sysPath.basename(file);
const parent = this.fsw._getWatchedDir(dirname);
// stats is always present
let prevStats = stats;
// if the file is already being watched, do nothing
if (parent.has(basename))
return;
const listener = async (path, newStats) => {
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
return;
if (!newStats || newStats.mtimeMs === 0) {
try {
const newStats = await (0, promises_1.stat)(file);
if (this.fsw.closed)
return;
// Check that change event was not fired because of changed only accessTime.
const at = newStats.atimeMs;
const mt = newStats.mtimeMs;
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit(EV.CHANGE, file, newStats);
}
if ((exports.isMacos || exports.isLinux) && prevStats.ino !== newStats.ino) {
this.fsw._closeFile(path);
prevStats = newStats;
const closer = this._watchWithNodeFs(file, listener);
if (closer)
this.fsw._addPathCloser(path, closer);
}
else {
prevStats = newStats;
}
}
catch (error) {
// Fix issues where mtime is null but file is still present
this.fsw._remove(dirname, basename);
}
// add is about to be emitted if file not already tracked in parent
}
else if (parent.has(basename)) {
// Check that change event was not fired because of changed only accessTime.
const at = newStats.atimeMs;
const mt = newStats.mtimeMs;
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit(EV.CHANGE, file, newStats);
}
prevStats = newStats;
}
};
// kick off the watcher
const closer = this._watchWithNodeFs(file, listener);
// emit an add event if we're supposed to
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
if (!this.fsw._throttle(EV.ADD, file, 0))
return;
this.fsw._emit(EV.ADD, file, stats);
}
return closer;
}
/**
* Handle symlinks encountered while reading a dir.
* @param entry returned by readdirp
* @param directory path of dir being read
* @param path of this item
* @param item basename of this item
* @returns true if no more processing is needed for this entry.
*/
async _handleSymlink(entry, directory, path, item) {
if (this.fsw.closed) {
return;
}
const full = entry.fullPath;
const dir = this.fsw._getWatchedDir(directory);
if (!this.fsw.options.followSymlinks) {
// watch symlink directly (don't follow) and detect changes
this.fsw._incrReadyCount();
let linkPath;
try {
linkPath = await (0, promises_1.realpath)(path);
}
catch (e) {
this.fsw._emitReady();
return true;
}
if (this.fsw.closed)
return;
if (dir.has(item)) {
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit(EV.CHANGE, path, entry.stats);
}
}
else {
dir.add(item);
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit(EV.ADD, path, entry.stats);
}
this.fsw._emitReady();
return true;
}
// don't follow the same symlink more than once
if (this.fsw._symlinkPaths.has(full)) {
return true;
}
this.fsw._symlinkPaths.set(full, true);
}
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
// Normalize the directory name on Windows
directory = sysPath.join(directory, '');
throttler = this.fsw._throttle('readdir', directory, 1000);
if (!throttler)
return;
const previous = this.fsw._getWatchedDir(wh.path);
const current = new Set();
let stream = this.fsw._readdirp(directory, {
fileFilter: (entry) => wh.filterPath(entry),
directoryFilter: (entry) => wh.filterDir(entry),
});
if (!stream)
return;
stream
.on(exports.STR_DATA, async (entry) => {
if (this.fsw.closed) {
stream = undefined;
return;
}
const item = entry.path;
let path = sysPath.join(directory, item);
current.add(item);
if (entry.stats.isSymbolicLink() &&
(await this._handleSymlink(entry, directory, path, item))) {
return;
}
if (this.fsw.closed) {
stream = undefined;
return;
}
// Files that present in current directory snapshot
// but absent in previous are added to watch list and
// emit `add` event.
if (item === target || (!target && !previous.has(item))) {
this.fsw._incrReadyCount();
// ensure relativeness of path is preserved in case of watcher reuse
path = sysPath.join(dir, sysPath.relative(dir, path));
this._addToNodeFs(path, initialAdd, wh, depth + 1);
}
})
.on(EV.ERROR, this._boundHandleError);
return new Promise((resolve, reject) => {
if (!stream)
return reject();
stream.once(exports.STR_END, () => {
if (this.fsw.closed) {
stream = undefined;
return;
}
const wasThrottled = throttler ? throttler.clear() : false;
resolve(undefined);
// Files that absent in current directory snapshot
// but present in previous emit `remove` event
// and are removed from @watched[directory].
previous
.getChildren()
.filter((item) => {
return item !== directory && !current.has(item);
})
.forEach((item) => {
this.fsw._remove(directory, item);
});
stream = undefined;
// one more time for any missed in case changes came in extremely quickly
if (wasThrottled)
this._handleRead(directory, false, wh, target, dir, depth, throttler);
});
});
}
/**
* Read directory to add / remove files from `@watched` list and re-read it on change.
* @param dir fs path
* @param stats
* @param initialAdd
* @param depth relative to user-supplied path
* @param target child path targeted for watch
* @param wh Common watch helpers for this path
* @param realpath
* @returns closer for the watcher instance.
*/
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {
const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
const tracked = parentDir.has(sysPath.basename(dir));
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
this.fsw._emit(EV.ADD_DIR, dir, stats);
}
// ensure dir is tracked (harmless if redundant)
parentDir.add(sysPath.basename(dir));
this.fsw._getWatchedDir(dir);
let throttler;
let closer;
const oDepth = this.fsw.options.depth;
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) {
if (!target) {
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
if (this.fsw.closed)
return;
}
closer = this._watchWithNodeFs(dir, (dirPath, stats) => {
// if current directory is removed, do nothing
if (stats && stats.mtimeMs === 0)
return;
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
});
}
return closer;
}
/**
* Handle added file, directory, or glob pattern.
* Delegates call to _handleFile / _handleDir after checks.
* @param path to file or ir
* @param initialAdd was the file added at watch instantiation?
* @param priorWh depth relative to user-supplied path
* @param depth Child path actually targeted for watch
* @param target Child path actually targeted for watch
*/
async _addToNodeFs(path, initialAdd, priorWh, depth, target) {
const ready = this.fsw._emitReady;
if (this.fsw._isIgnored(path) || this.fsw.closed) {
ready();
return false;
}
const wh = this.fsw._getWatchHelpers(path);
if (priorWh) {
wh.filterPath = (entry) => priorWh.filterPath(entry);
wh.filterDir = (entry) => priorWh.filterDir(entry);
}
// evaluate what is at the path we're being asked to watch
try {
const stats = await statMethods[wh.statMethod](wh.watchPath);
if (this.fsw.closed)
return;
if (this.fsw._isIgnored(wh.watchPath, stats)) {
ready();
return false;
}
const follow = this.fsw.options.followSymlinks;
let closer;
if (stats.isDirectory()) {
const absPath = sysPath.resolve(path);
const targetPath = follow ? await (0, promises_1.realpath)(path) : path;
if (this.fsw.closed)
return;
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
if (this.fsw.closed)
return;
// preserve this symlink's target path
if (absPath !== targetPath && targetPath !== undefined) {
this.fsw._symlinkPaths.set(absPath, targetPath);
}
}
else if (stats.isSymbolicLink()) {
const targetPath = follow ? await (0, promises_1.realpath)(path) : path;
if (this.fsw.closed)
return;
const parent = sysPath.dirname(wh.watchPath);
this.fsw._getWatchedDir(parent).add(wh.watchPath);
this.fsw._emit(EV.ADD, wh.watchPath, stats);
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
if (this.fsw.closed)
return;
// preserve this symlink's target path
if (targetPath !== undefined) {
this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
}
}
else {
closer = this._handleFile(wh.watchPath, stats, initialAdd);
}
ready();
if (closer)
this.fsw._addPathCloser(path, closer);
return false;
}
catch (error) {
if (this.fsw._handleError(error)) {
ready();
return path;
}
}
}
}
exports.NodeFsHandler = NodeFsHandler;
//# sourceMappingURL=handler.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,204 @@
import { Stats } from 'fs';
import { EventEmitter } from 'events';
import { ReaddirpStream, ReaddirpOptions, EntryInfo } from 'readdirp';
import { NodeFsHandler, EventName, Path } from './handler.js';
type AWF = {
stabilityThreshold: number;
pollInterval: number;
};
type BasicOpts = {
persistent: boolean;
ignoreInitial: boolean;
followSymlinks: boolean;
cwd?: string;
usePolling: boolean;
interval: number;
binaryInterval: number;
alwaysStat?: boolean;
depth?: number;
ignorePermissionErrors: boolean;
atomic: boolean | number;
};
export type Throttler = {
timeoutObject: NodeJS.Timeout;
clear: () => void;
count: number;
};
export type ChokidarOptions = Partial<BasicOpts & {
ignored: Matcher | Matcher[];
awaitWriteFinish: boolean | Partial<AWF>;
}>;
export type FSWInstanceOptions = BasicOpts & {
ignored: Matcher[];
awaitWriteFinish: false | AWF;
};
export type ThrottleType = 'readdir' | 'watch' | 'add' | 'remove' | 'change';
export type EmitArgs = [EventName, Path | Error, any?, any?, any?];
export type MatchFunction = (val: string, stats?: Stats) => boolean;
export interface MatcherObject {
path: string;
recursive?: boolean;
}
export type Matcher = string | RegExp | MatchFunction | MatcherObject;
/**
* Directory entry.
*/
declare class DirEntry {
path: Path;
_removeWatcher: (dir: string, base: string) => void;
items: Set<Path>;
constructor(dir: Path, removeWatcher: (dir: string, base: string) => void);
add(item: string): void;
remove(item: string): Promise<void>;
has(item: string): boolean | undefined;
getChildren(): string[];
dispose(): void;
}
export declare class WatchHelper {
fsw: FSWatcher;
path: string;
watchPath: string;
fullWatchPath: string;
dirParts: string[][];
followSymlinks: boolean;
statMethod: 'stat' | 'lstat';
constructor(path: string, follow: boolean, fsw: FSWatcher);
entryPath(entry: EntryInfo): Path;
filterPath(entry: EntryInfo): boolean;
filterDir(entry: EntryInfo): boolean;
}
/**
* Watches files & directories for changes. Emitted events:
* `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
*
* new FSWatcher()
* .add(directories)
* .on('add', path => log('File', path, 'was added'))
*/
export declare class FSWatcher extends EventEmitter {
closed: boolean;
options: FSWInstanceOptions;
_closers: Map<string, Array<any>>;
_ignoredPaths: Set<Matcher>;
_throttled: Map<ThrottleType, Map<any, any>>;
_streams: Set<ReaddirpStream>;
_symlinkPaths: Map<Path, string | boolean>;
_watched: Map<string, DirEntry>;
_pendingWrites: Map<string, any>;
_pendingUnlinks: Map<string, EmitArgs>;
_readyCount: number;
_emitReady: () => void;
_closePromise?: Promise<void>;
_userIgnored?: MatchFunction;
_readyEmitted: boolean;
_emitRaw: () => void;
_boundRemove: (dir: string, item: string) => void;
_nodeFsHandler: NodeFsHandler;
constructor(_opts?: ChokidarOptions);
_addIgnoredPath(matcher: Matcher): void;
_removeIgnoredPath(matcher: Matcher): void;
/**
* Adds paths to be watched on an existing FSWatcher instance.
* @param paths_ file or file list. Other arguments are unused
*/
add(paths_: Path | Path[], _origAdd?: string, _internal?: boolean): FSWatcher;
/**
* Close watchers or start ignoring events from specified paths.
*/
unwatch(paths_: Path | Path[]): FSWatcher;
/**
* Close watchers and remove all listeners from watched paths.
*/
close(): Promise<void>;
/**
* Expose list of watched paths
* @returns for chaining
*/
getWatched(): Record<string, string[]>;
emitWithAll(event: EventName, args: EmitArgs): void;
/**
* Normalize and emit events.
* Calling _emit DOES NOT MEAN emit() would be called!
* @param event Type of event
* @param path File or directory path
* @param stats arguments to be passed with event
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
_emit(event: EventName, path: Path, stats?: Stats): Promise<this | undefined>;
/**
* Common handler for errors
* @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
_handleError(error: Error): Error | boolean;
/**
* Helper utility for throttling
* @param actionType type being throttled
* @param path being acted upon
* @param timeout duration of time to suppress duplicate actions
* @returns tracking object or false if action should be suppressed
*/
_throttle(actionType: ThrottleType, path: Path, timeout: number): Throttler | false;
_incrReadyCount(): number;
/**
* Awaits write operation to finish.
* Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
* @param path being acted upon
* @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
* @param event
* @param awfEmit Callback to be called when ready for event to be emitted.
*/
_awaitWriteFinish(path: Path, threshold: number, event: EventName, awfEmit: (err?: Error, stat?: Stats) => void): void;
/**
* Determines whether user has asked to ignore this path.
*/
_isIgnored(path: Path, stats?: Stats): boolean;
_isntIgnored(path: Path, stat?: Stats): boolean;
/**
* Provides a set of common helpers and properties relating to symlink handling.
* @param path file or directory pattern being watched
*/
_getWatchHelpers(path: Path): WatchHelper;
/**
* Provides directory tracking objects
* @param directory path of the directory
*/
_getWatchedDir(directory: string): DirEntry;
/**
* Check for read permissions: https://stackoverflow.com/a/11781404/1358405
*/
_hasReadPermissions(stats: Stats): boolean;
/**
* Handles emitting unlink events for
* files and directories, and via recursion, for
* files and directories within directories that are unlinked
* @param directory within which the following item is located
* @param item base path of item/directory
*/
_remove(directory: string, item: string, isDirectory?: boolean): void;
/**
* Closes all watchers for a path
*/
_closePath(path: Path): void;
/**
* Closes only file-specific watchers
*/
_closeFile(path: Path): void;
_addPathCloser(path: Path, closer: () => void): void;
_readdirp(root: Path, opts?: Partial<ReaddirpOptions>): ReaddirpStream | undefined;
}
/**
* Instantiates watcher with paths to be tracked.
* @param paths file / directory paths
* @param options opts, such as `atomic`, `awaitWriteFinish`, `ignored`, and others
* @returns an instance of FSWatcher for chaining.
* @example
* const watcher = watch('.').on('all', (event, path) => { console.log(event, path); });
* watch('.', { atomic: true, awaitWriteFinish: true, ignored: (f, stats) => stats?.isFile() && !f.endsWith('.js') })
*/
export declare function watch(paths: string | string[], options?: ChokidarOptions): FSWatcher;
declare const _default: {
watch: typeof watch;
FSWatcher: typeof FSWatcher;
};
export default _default;
//# sourceMappingURL=index.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"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"}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,66 +0,0 @@
'use strict';
const {sep} = require('path');
const {platform} = process;
const os = require('os');
exports.EV_ALL = 'all';
exports.EV_READY = 'ready';
exports.EV_ADD = 'add';
exports.EV_CHANGE = 'change';
exports.EV_ADD_DIR = 'addDir';
exports.EV_UNLINK = 'unlink';
exports.EV_UNLINK_DIR = 'unlinkDir';
exports.EV_RAW = 'raw';
exports.EV_ERROR = 'error';
exports.STR_DATA = 'data';
exports.STR_END = 'end';
exports.STR_CLOSE = 'close';
exports.FSEVENT_CREATED = 'created';
exports.FSEVENT_MODIFIED = 'modified';
exports.FSEVENT_DELETED = 'deleted';
exports.FSEVENT_MOVED = 'moved';
exports.FSEVENT_CLONED = 'cloned';
exports.FSEVENT_UNKNOWN = 'unknown';
exports.FSEVENT_FLAG_MUST_SCAN_SUBDIRS = 1;
exports.FSEVENT_TYPE_FILE = 'file';
exports.FSEVENT_TYPE_DIRECTORY = 'directory';
exports.FSEVENT_TYPE_SYMLINK = 'symlink';
exports.KEY_LISTENERS = 'listeners';
exports.KEY_ERR = 'errHandlers';
exports.KEY_RAW = 'rawEmitters';
exports.HANDLER_KEYS = [exports.KEY_LISTENERS, exports.KEY_ERR, exports.KEY_RAW];
exports.DOT_SLASH = `.${sep}`;
exports.BACK_SLASH_RE = /\\/g;
exports.DOUBLE_SLASH_RE = /\/\//;
exports.SLASH_OR_BACK_SLASH_RE = /[/\\]/;
exports.DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
exports.REPLACER_RE = /^\.[/\\]/;
exports.SLASH = '/';
exports.SLASH_SLASH = '//';
exports.BRACE_START = '{';
exports.BANG = '!';
exports.ONE_DOT = '.';
exports.TWO_DOTS = '..';
exports.STAR = '*';
exports.GLOBSTAR = '**';
exports.ROOT_GLOBSTAR = '/**/*';
exports.SLASH_GLOBSTAR = '/**';
exports.DIR_SUFFIX = 'Dir';
exports.ANYMATCH_OPTS = {dot: true};
exports.STRING_TYPE = 'string';
exports.FUNCTION_TYPE = 'function';
exports.EMPTY_STR = '';
exports.EMPTY_FN = () => {};
exports.IDENTITY_FN = val => val;
exports.isWindows = platform === 'win32';
exports.isMacos = platform === 'darwin';
exports.isLinux = platform === 'linux';
exports.isIBMi = os.type() === 'OS400';

View File

@ -1,526 +0,0 @@
'use strict';
const fs = require('fs');
const sysPath = require('path');
const { promisify } = require('util');
let fsevents;
try {
fsevents = require('fsevents');
} catch (error) {
if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) console.error(error);
}
if (fsevents) {
// TODO: real check
const mtch = process.version.match(/v(\d+)\.(\d+)/);
if (mtch && mtch[1] && mtch[2]) {
const maj = Number.parseInt(mtch[1], 10);
const min = Number.parseInt(mtch[2], 10);
if (maj === 8 && min < 16) {
fsevents = undefined;
}
}
}
const {
EV_ADD,
EV_CHANGE,
EV_ADD_DIR,
EV_UNLINK,
EV_ERROR,
STR_DATA,
STR_END,
FSEVENT_CREATED,
FSEVENT_MODIFIED,
FSEVENT_DELETED,
FSEVENT_MOVED,
// FSEVENT_CLONED,
FSEVENT_UNKNOWN,
FSEVENT_FLAG_MUST_SCAN_SUBDIRS,
FSEVENT_TYPE_FILE,
FSEVENT_TYPE_DIRECTORY,
FSEVENT_TYPE_SYMLINK,
ROOT_GLOBSTAR,
DIR_SUFFIX,
DOT_SLASH,
FUNCTION_TYPE,
EMPTY_FN,
IDENTITY_FN
} = require('./constants');
const Depth = (value) => isNaN(value) ? {} : {depth: value};
const stat = promisify(fs.stat);
const lstat = promisify(fs.lstat);
const realpath = promisify(fs.realpath);
const statMethods = { stat, lstat };
/**
* @typedef {String} Path
*/
/**
* @typedef {Object} FsEventsWatchContainer
* @property {Set<Function>} listeners
* @property {Function} rawEmitter
* @property {{stop: Function}} watcher
*/
// fsevents instance helper functions
/**
* Object to hold per-process fsevents instances (may be shared across chokidar FSWatcher instances)
* @type {Map<Path,FsEventsWatchContainer>}
*/
const FSEventsWatchers = new Map();
// Threshold of duplicate path prefixes at which to start
// consolidating going forward
const consolidateThreshhold = 10;
const wrongEventFlags = new Set([
69888, 70400, 71424, 72704, 73472, 131328, 131840, 262912
]);
/**
* Instantiates the fsevents interface
* @param {Path} path path to be watched
* @param {Function} callback called when fsevents is bound and ready
* @returns {{stop: Function}} new fsevents instance
*/
const createFSEventsInstance = (path, callback) => {
const stop = fsevents.watch(path, callback);
return {stop};
};
/**
* Instantiates the fsevents interface or binds listeners to an existing one covering
* the same file tree.
* @param {Path} path - to be watched
* @param {Path} realPath - real path for symlinks
* @param {Function} listener - called when fsevents emits events
* @param {Function} rawEmitter - passes data to listeners of the 'raw' event
* @returns {Function} closer
*/
function setFSEventsListener(path, realPath, listener, rawEmitter) {
let watchPath = sysPath.extname(realPath) ? sysPath.dirname(realPath) : realPath;
const parentPath = sysPath.dirname(watchPath);
let cont = FSEventsWatchers.get(watchPath);
// If we've accumulated a substantial number of paths that
// could have been consolidated by watching one directory
// above the current one, create a watcher on the parent
// path instead, so that we do consolidate going forward.
if (couldConsolidate(parentPath)) {
watchPath = parentPath;
}
const resolvedPath = sysPath.resolve(path);
const hasSymlink = resolvedPath !== realPath;
const filteredListener = (fullPath, flags, info) => {
if (hasSymlink) fullPath = fullPath.replace(realPath, resolvedPath);
if (
fullPath === resolvedPath ||
!fullPath.indexOf(resolvedPath + sysPath.sep)
) listener(fullPath, flags, info);
};
// check if there is already a watcher on a parent path
// modifies `watchPath` to the parent path when it finds a match
let watchedParent = false;
for (const watchedPath of FSEventsWatchers.keys()) {
if (realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep) === 0) {
watchPath = watchedPath;
cont = FSEventsWatchers.get(watchPath);
watchedParent = true;
break;
}
}
if (cont || watchedParent) {
cont.listeners.add(filteredListener);
} else {
cont = {
listeners: new Set([filteredListener]),
rawEmitter,
watcher: createFSEventsInstance(watchPath, (fullPath, flags) => {
if (!cont.listeners.size) return;
if (flags & FSEVENT_FLAG_MUST_SCAN_SUBDIRS) return;
const info = fsevents.getInfo(fullPath, flags);
cont.listeners.forEach(list => {
list(fullPath, flags, info);
});
cont.rawEmitter(info.event, fullPath, info);
})
};
FSEventsWatchers.set(watchPath, cont);
}
// removes this instance's listeners and closes the underlying fsevents
// instance if there are no more listeners left
return () => {
const lst = cont.listeners;
lst.delete(filteredListener);
if (!lst.size) {
FSEventsWatchers.delete(watchPath);
if (cont.watcher) return cont.watcher.stop().then(() => {
cont.rawEmitter = cont.watcher = undefined;
Object.freeze(cont);
});
}
};
}
// Decide whether or not we should start a new higher-level
// parent watcher
const couldConsolidate = (path) => {
let count = 0;
for (const watchPath of FSEventsWatchers.keys()) {
if (watchPath.indexOf(path) === 0) {
count++;
if (count >= consolidateThreshhold) {
return true;
}
}
}
return false;
};
// returns boolean indicating whether fsevents can be used
const canUse = () => fsevents && FSEventsWatchers.size < 128;
// determines subdirectory traversal levels from root to path
const calcDepth = (path, root) => {
let i = 0;
while (!path.indexOf(root) && (path = sysPath.dirname(path)) !== root) i++;
return i;
};
// returns boolean indicating whether the fsevents' event info has the same type
// as the one returned by fs.stat
const sameTypes = (info, stats) => (
info.type === FSEVENT_TYPE_DIRECTORY && stats.isDirectory() ||
info.type === FSEVENT_TYPE_SYMLINK && stats.isSymbolicLink() ||
info.type === FSEVENT_TYPE_FILE && stats.isFile()
)
/**
* @mixin
*/
class FsEventsHandler {
/**
* @param {import('../index').FSWatcher} fsw
*/
constructor(fsw) {
this.fsw = fsw;
}
checkIgnored(path, stats) {
const ipaths = this.fsw._ignoredPaths;
if (this.fsw._isIgnored(path, stats)) {
ipaths.add(path);
if (stats && stats.isDirectory()) {
ipaths.add(path + ROOT_GLOBSTAR);
}
return true;
}
ipaths.delete(path);
ipaths.delete(path + ROOT_GLOBSTAR);
}
addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
const event = watchedDir.has(item) ? EV_CHANGE : EV_ADD;
this.handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts);
}
async checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
try {
const stats = await stat(path)
if (this.fsw.closed) return;
if (sameTypes(info, stats)) {
this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
} else {
this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
}
} catch (error) {
if (error.code === 'EACCES') {
this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
} else {
this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
}
}
}
handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts) {
if (this.fsw.closed || this.checkIgnored(path)) return;
if (event === EV_UNLINK) {
const isDirectory = info.type === FSEVENT_TYPE_DIRECTORY
// suppress unlink events on never before seen files
if (isDirectory || watchedDir.has(item)) {
this.fsw._remove(parent, item, isDirectory);
}
} else {
if (event === EV_ADD) {
// track new directories
if (info.type === FSEVENT_TYPE_DIRECTORY) this.fsw._getWatchedDir(path);
if (info.type === FSEVENT_TYPE_SYMLINK && opts.followSymlinks) {
// push symlinks back to the top of the stack to get handled
const curDepth = opts.depth === undefined ?
undefined : calcDepth(fullPath, realPath) + 1;
return this._addToFsEvents(path, false, true, curDepth);
}
// track new paths
// (other than symlinks being followed, which will be tracked soon)
this.fsw._getWatchedDir(parent).add(item);
}
/**
* @type {'add'|'addDir'|'unlink'|'unlinkDir'}
*/
const eventName = info.type === FSEVENT_TYPE_DIRECTORY ? event + DIR_SUFFIX : event;
this.fsw._emit(eventName, path);
if (eventName === EV_ADD_DIR) this._addToFsEvents(path, false, true);
}
}
/**
* Handle symlinks encountered during directory scan
* @param {String} watchPath - file/dir path to be watched with fsevents
* @param {String} realPath - real path (in case of symlinks)
* @param {Function} transform - path transformer
* @param {Function} globFilter - path filter in case a glob pattern was provided
* @returns {Function} closer for the watcher instance
*/
_watchWithFsEvents(watchPath, realPath, transform, globFilter) {
if (this.fsw.closed || this.fsw._isIgnored(watchPath)) return;
const opts = this.fsw.options;
const watchCallback = async (fullPath, flags, info) => {
if (this.fsw.closed) return;
if (
opts.depth !== undefined &&
calcDepth(fullPath, realPath) > opts.depth
) return;
const path = transform(sysPath.join(
watchPath, sysPath.relative(watchPath, fullPath)
));
if (globFilter && !globFilter(path)) return;
// ensure directories are tracked
const parent = sysPath.dirname(path);
const item = sysPath.basename(path);
const watchedDir = this.fsw._getWatchedDir(
info.type === FSEVENT_TYPE_DIRECTORY ? path : parent
);
// correct for wrong events emitted
if (wrongEventFlags.has(flags) || info.event === FSEVENT_UNKNOWN) {
if (typeof opts.ignored === FUNCTION_TYPE) {
let stats;
try {
stats = await stat(path);
} catch (error) {}
if (this.fsw.closed) return;
if (this.checkIgnored(path, stats)) return;
if (sameTypes(info, stats)) {
this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
} else {
this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
}
} else {
this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts);
}
} else {
switch (info.event) {
case FSEVENT_CREATED:
case FSEVENT_MODIFIED:
return this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
case FSEVENT_DELETED:
case FSEVENT_MOVED:
return this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts);
}
}
};
const closer = setFSEventsListener(
watchPath,
realPath,
watchCallback,
this.fsw._emitRaw
);
this.fsw._emitReady();
return closer;
}
/**
* Handle symlinks encountered during directory scan
* @param {String} linkPath path to symlink
* @param {String} fullPath absolute path to the symlink
* @param {Function} transform pre-existing path transformer
* @param {Number} curDepth level of subdirectories traversed to where symlink is
* @returns {Promise<void>}
*/
async _handleFsEventsSymlink(linkPath, fullPath, transform, curDepth) {
// don't follow the same symlink more than once
if (this.fsw.closed || this.fsw._symlinkPaths.has(fullPath)) return;
this.fsw._symlinkPaths.set(fullPath, true);
this.fsw._incrReadyCount();
try {
const linkTarget = await realpath(linkPath);
if (this.fsw.closed) return;
if (this.fsw._isIgnored(linkTarget)) {
return this.fsw._emitReady();
}
this.fsw._incrReadyCount();
// add the linkTarget for watching with a wrapper for transform
// that causes emitted paths to incorporate the link's path
this._addToFsEvents(linkTarget || linkPath, (path) => {
let aliasedPath = linkPath;
if (linkTarget && linkTarget !== DOT_SLASH) {
aliasedPath = path.replace(linkTarget, linkPath);
} else if (path !== DOT_SLASH) {
aliasedPath = sysPath.join(linkPath, path);
}
return transform(aliasedPath);
}, false, curDepth);
} catch(error) {
if (this.fsw._handleError(error)) {
return this.fsw._emitReady();
}
}
}
/**
*
* @param {Path} newPath
* @param {fs.Stats} stats
*/
emitAdd(newPath, stats, processPath, opts, forceAdd) {
const pp = processPath(newPath);
const isDir = stats.isDirectory();
const dirObj = this.fsw._getWatchedDir(sysPath.dirname(pp));
const base = sysPath.basename(pp);
// ensure empty dirs get tracked
if (isDir) this.fsw._getWatchedDir(pp);
if (dirObj.has(base)) return;
dirObj.add(base);
if (!opts.ignoreInitial || forceAdd === true) {
this.fsw._emit(isDir ? EV_ADD_DIR : EV_ADD, pp, stats);
}
}
initWatch(realPath, path, wh, processPath) {
if (this.fsw.closed) return;
const closer = this._watchWithFsEvents(
wh.watchPath,
sysPath.resolve(realPath || wh.watchPath),
processPath,
wh.globFilter
);
this.fsw._addPathCloser(path, closer);
}
/**
* Handle added path with fsevents
* @param {String} path file/dir path or glob pattern
* @param {Function|Boolean=} transform converts working path to what the user expects
* @param {Boolean=} forceAdd ensure add is emitted
* @param {Number=} priorDepth Level of subdirectories already traversed.
* @returns {Promise<void>}
*/
async _addToFsEvents(path, transform, forceAdd, priorDepth) {
if (this.fsw.closed) {
return;
}
const opts = this.fsw.options;
const processPath = typeof transform === FUNCTION_TYPE ? transform : IDENTITY_FN;
const wh = this.fsw._getWatchHelpers(path);
// evaluate what is at the path we're being asked to watch
try {
const stats = await statMethods[wh.statMethod](wh.watchPath);
if (this.fsw.closed) return;
if (this.fsw._isIgnored(wh.watchPath, stats)) {
throw null;
}
if (stats.isDirectory()) {
// emit addDir unless this is a glob parent
if (!wh.globFilter) this.emitAdd(processPath(path), stats, processPath, opts, forceAdd);
// don't recurse further if it would exceed depth setting
if (priorDepth && priorDepth > opts.depth) return;
// scan the contents of the dir
this.fsw._readdirp(wh.watchPath, {
fileFilter: entry => wh.filterPath(entry),
directoryFilter: entry => wh.filterDir(entry),
...Depth(opts.depth - (priorDepth || 0))
}).on(STR_DATA, (entry) => {
// need to check filterPath on dirs b/c filterDir is less restrictive
if (this.fsw.closed) {
return;
}
if (entry.stats.isDirectory() && !wh.filterPath(entry)) return;
const joinedPath = sysPath.join(wh.watchPath, entry.path);
const {fullPath} = entry;
if (wh.followSymlinks && entry.stats.isSymbolicLink()) {
// preserve the current depth here since it can't be derived from
// real paths past the symlink
const curDepth = opts.depth === undefined ?
undefined : calcDepth(joinedPath, sysPath.resolve(wh.watchPath)) + 1;
this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth);
} else {
this.emitAdd(joinedPath, entry.stats, processPath, opts, forceAdd);
}
}).on(EV_ERROR, EMPTY_FN).on(STR_END, () => {
this.fsw._emitReady();
});
} else {
this.emitAdd(wh.watchPath, stats, processPath, opts, forceAdd);
this.fsw._emitReady();
}
} catch (error) {
if (!error || this.fsw._handleError(error)) {
// TODO: Strange thing: "should not choke on an ignored watch path" will be failed without 2 ready calls -__-
this.fsw._emitReady();
this.fsw._emitReady();
}
}
if (opts.persistent && forceAdd !== true) {
if (typeof transform === FUNCTION_TYPE) {
// realpath has already been resolved
this.initWatch(undefined, path, wh, processPath);
} else {
let realPath;
try {
realPath = await realpath(wh.watchPath);
} catch (e) {}
this.initWatch(realPath, path, wh, processPath);
}
}
}
}
module.exports = FsEventsHandler;
module.exports.canUse = canUse;

View File

@ -1,654 +0,0 @@
'use strict';
const fs = require('fs');
const sysPath = require('path');
const { promisify } = require('util');
const isBinaryPath = require('is-binary-path');
const {
isWindows,
isLinux,
EMPTY_FN,
EMPTY_STR,
KEY_LISTENERS,
KEY_ERR,
KEY_RAW,
HANDLER_KEYS,
EV_CHANGE,
EV_ADD,
EV_ADD_DIR,
EV_ERROR,
STR_DATA,
STR_END,
BRACE_START,
STAR
} = require('./constants');
const THROTTLE_MODE_WATCH = 'watch';
const open = promisify(fs.open);
const stat = promisify(fs.stat);
const lstat = promisify(fs.lstat);
const close = promisify(fs.close);
const fsrealpath = promisify(fs.realpath);
const statMethods = { lstat, stat };
// TODO: emit errors properly. Example: EMFILE on Macos.
const foreach = (val, fn) => {
if (val instanceof Set) {
val.forEach(fn);
} else {
fn(val);
}
};
const addAndConvert = (main, prop, item) => {
let container = main[prop];
if (!(container instanceof Set)) {
main[prop] = container = new Set([container]);
}
container.add(item);
};
const clearItem = cont => key => {
const set = cont[key];
if (set instanceof Set) {
set.clear();
} else {
delete cont[key];
}
};
const delFromSet = (main, prop, item) => {
const container = main[prop];
if (container instanceof Set) {
container.delete(item);
} else if (container === item) {
delete main[prop];
}
};
const isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
/**
* @typedef {String} Path
*/
// fs_watch helpers
// object to hold per-process fs_watch instances
// (may be shared across chokidar FSWatcher instances)
/**
* @typedef {Object} FsWatchContainer
* @property {Set} listeners
* @property {Set} errHandlers
* @property {Set} rawEmitters
* @property {fs.FSWatcher=} watcher
* @property {Boolean=} watcherUnusable
*/
/**
* @type {Map<String,FsWatchContainer>}
*/
const FsWatchInstances = new Map();
/**
* Instantiates the fs_watch interface
* @param {String} path to be watched
* @param {Object} options to be passed to fs_watch
* @param {Function} listener main event handler
* @param {Function} errHandler emits info about errors
* @param {Function} emitRaw emits raw event data
* @returns {fs.FSWatcher} new fsevents instance
*/
function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
const handleEvent = (rawEvent, evPath) => {
listener(path);
emitRaw(rawEvent, evPath, {watchedPath: path});
// emit based on events occurring for files from a directory's watcher in
// case the file's watcher misses it (and rely on throttling to de-dupe)
if (evPath && path !== evPath) {
fsWatchBroadcast(
sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath)
);
}
};
try {
return fs.watch(path, options, handleEvent);
} catch (error) {
errHandler(error);
}
}
/**
* Helper for passing fs_watch event data to a collection of listeners
* @param {Path} fullPath absolute path bound to fs_watch instance
* @param {String} type listener type
* @param {*=} val1 arguments to be passed to listeners
* @param {*=} val2
* @param {*=} val3
*/
const fsWatchBroadcast = (fullPath, type, val1, val2, val3) => {
const cont = FsWatchInstances.get(fullPath);
if (!cont) return;
foreach(cont[type], (listener) => {
listener(val1, val2, val3);
});
};
/**
* Instantiates the fs_watch interface or binds listeners
* to an existing one covering the same file system entry
* @param {String} path
* @param {String} fullPath absolute path
* @param {Object} options to be passed to fs_watch
* @param {Object} handlers container for event listener functions
*/
const setFsWatchListener = (path, fullPath, options, handlers) => {
const {listener, errHandler, rawEmitter} = handlers;
let cont = FsWatchInstances.get(fullPath);
/** @type {fs.FSWatcher=} */
let watcher;
if (!options.persistent) {
watcher = createFsWatchInstance(
path, options, listener, errHandler, rawEmitter
);
return watcher.close.bind(watcher);
}
if (cont) {
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_ERR, errHandler);
addAndConvert(cont, KEY_RAW, rawEmitter);
} else {
watcher = createFsWatchInstance(
path,
options,
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
errHandler, // no need to use broadcast here
fsWatchBroadcast.bind(null, fullPath, KEY_RAW)
);
if (!watcher) return;
watcher.on(EV_ERROR, async (error) => {
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
cont.watcherUnusable = true; // documented since Node 10.4.1
// Workaround for https://github.com/joyent/node/issues/4337
if (isWindows && error.code === 'EPERM') {
try {
const fd = await open(path, 'r');
await close(fd);
broadcastErr(error);
} catch (err) {}
} else {
broadcastErr(error);
}
});
cont = {
listeners: listener,
errHandlers: errHandler,
rawEmitters: rawEmitter,
watcher
};
FsWatchInstances.set(fullPath, cont);
}
// const index = cont.listeners.indexOf(listener);
// removes this instance's listeners and closes the underlying fs_watch
// instance if there are no more listeners left
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_ERR, errHandler);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
// Check to protect against issue gh-730.
// if (cont.watcherUnusable) {
cont.watcher.close();
// }
FsWatchInstances.delete(fullPath);
HANDLER_KEYS.forEach(clearItem(cont));
cont.watcher = undefined;
Object.freeze(cont);
}
};
};
// fs_watchFile helpers
// object to hold per-process fs_watchFile instances
// (may be shared across chokidar FSWatcher instances)
const FsWatchFileInstances = new Map();
/**
* Instantiates the fs_watchFile interface or binds listeners
* to an existing one covering the same file system entry
* @param {String} path to be watched
* @param {String} fullPath absolute path
* @param {Object} options options to be passed to fs_watchFile
* @param {Object} handlers container for event listener functions
* @returns {Function} closer
*/
const setFsWatchFileListener = (path, fullPath, options, handlers) => {
const {listener, rawEmitter} = handlers;
let cont = FsWatchFileInstances.get(fullPath);
/* eslint-disable no-unused-vars, prefer-destructuring */
let listeners = new Set();
let rawEmitters = new Set();
const copts = cont && cont.options;
if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
// "Upgrade" the watcher to persistence or a quicker interval.
// This creates some unlikely edge case issues if the user mixes
// settings in a very weird way, but solving for those cases
// doesn't seem worthwhile for the added complexity.
listeners = cont.listeners;
rawEmitters = cont.rawEmitters;
fs.unwatchFile(fullPath);
cont = undefined;
}
/* eslint-enable no-unused-vars, prefer-destructuring */
if (cont) {
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_RAW, rawEmitter);
} else {
// TODO
// listeners.add(listener);
// rawEmitters.add(rawEmitter);
cont = {
listeners: listener,
rawEmitters: rawEmitter,
options,
watcher: fs.watchFile(fullPath, options, (curr, prev) => {
foreach(cont.rawEmitters, (rawEmitter) => {
rawEmitter(EV_CHANGE, fullPath, {curr, prev});
});
const currmtime = curr.mtimeMs;
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
foreach(cont.listeners, (listener) => listener(path, curr));
}
})
};
FsWatchFileInstances.set(fullPath, cont);
}
// const index = cont.listeners.indexOf(listener);
// Removes this instance's listeners and closes the underlying fs_watchFile
// instance if there are no more listeners left.
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
FsWatchFileInstances.delete(fullPath);
fs.unwatchFile(fullPath);
cont.options = cont.watcher = undefined;
Object.freeze(cont);
}
};
};
/**
* @mixin
*/
class NodeFsHandler {
/**
* @param {import("../index").FSWatcher} fsW
*/
constructor(fsW) {
this.fsw = fsW;
this._boundHandleError = (error) => fsW._handleError(error);
}
/**
* Watch file for changes with fs_watchFile or fs_watch.
* @param {String} path to file or dir
* @param {Function} listener on fs change
* @returns {Function} closer for the watcher instance
*/
_watchWithNodeFs(path, listener) {
const opts = this.fsw.options;
const directory = sysPath.dirname(path);
const basename = sysPath.basename(path);
const parent = this.fsw._getWatchedDir(directory);
parent.add(basename);
const absolutePath = sysPath.resolve(path);
const options = {persistent: opts.persistent};
if (!listener) listener = EMPTY_FN;
let closer;
if (opts.usePolling) {
options.interval = opts.enableBinaryInterval && isBinaryPath(basename) ?
opts.binaryInterval : opts.interval;
closer = setFsWatchFileListener(path, absolutePath, options, {
listener,
rawEmitter: this.fsw._emitRaw
});
} else {
closer = setFsWatchListener(path, absolutePath, options, {
listener,
errHandler: this._boundHandleError,
rawEmitter: this.fsw._emitRaw
});
}
return closer;
}
/**
* Watch a file and emit add event if warranted.
* @param {Path} file Path
* @param {fs.Stats} stats result of fs_stat
* @param {Boolean} initialAdd was the file added at watch instantiation?
* @returns {Function} closer for the watcher instance
*/
_handleFile(file, stats, initialAdd) {
if (this.fsw.closed) {
return;
}
const dirname = sysPath.dirname(file);
const basename = sysPath.basename(file);
const parent = this.fsw._getWatchedDir(dirname);
// stats is always present
let prevStats = stats;
// if the file is already being watched, do nothing
if (parent.has(basename)) return;
const listener = async (path, newStats) => {
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5)) return;
if (!newStats || newStats.mtimeMs === 0) {
try {
const newStats = await stat(file);
if (this.fsw.closed) return;
// Check that change event was not fired because of changed only accessTime.
const at = newStats.atimeMs;
const mt = newStats.mtimeMs;
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit(EV_CHANGE, file, newStats);
}
if (isLinux && prevStats.ino !== newStats.ino) {
this.fsw._closeFile(path)
prevStats = newStats;
this.fsw._addPathCloser(path, this._watchWithNodeFs(file, listener));
} else {
prevStats = newStats;
}
} catch (error) {
// Fix issues where mtime is null but file is still present
this.fsw._remove(dirname, basename);
}
// add is about to be emitted if file not already tracked in parent
} else if (parent.has(basename)) {
// Check that change event was not fired because of changed only accessTime.
const at = newStats.atimeMs;
const mt = newStats.mtimeMs;
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit(EV_CHANGE, file, newStats);
}
prevStats = newStats;
}
}
// kick off the watcher
const closer = this._watchWithNodeFs(file, listener);
// emit an add event if we're supposed to
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
if (!this.fsw._throttle(EV_ADD, file, 0)) return;
this.fsw._emit(EV_ADD, file, stats);
}
return closer;
}
/**
* Handle symlinks encountered while reading a dir.
* @param {Object} entry returned by readdirp
* @param {String} directory path of dir being read
* @param {String} path of this item
* @param {String} item basename of this item
* @returns {Promise<Boolean>} true if no more processing is needed for this entry.
*/
async _handleSymlink(entry, directory, path, item) {
if (this.fsw.closed) {
return;
}
const full = entry.fullPath;
const dir = this.fsw._getWatchedDir(directory);
if (!this.fsw.options.followSymlinks) {
// watch symlink directly (don't follow) and detect changes
this.fsw._incrReadyCount();
let linkPath;
try {
linkPath = await fsrealpath(path);
} catch (e) {
this.fsw._emitReady();
return true;
}
if (this.fsw.closed) return;
if (dir.has(item)) {
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit(EV_CHANGE, path, entry.stats);
}
} else {
dir.add(item);
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit(EV_ADD, path, entry.stats);
}
this.fsw._emitReady();
return true;
}
// don't follow the same symlink more than once
if (this.fsw._symlinkPaths.has(full)) {
return true;
}
this.fsw._symlinkPaths.set(full, true);
}
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
// Normalize the directory name on Windows
directory = sysPath.join(directory, EMPTY_STR);
if (!wh.hasGlob) {
throttler = this.fsw._throttle('readdir', directory, 1000);
if (!throttler) return;
}
const previous = this.fsw._getWatchedDir(wh.path);
const current = new Set();
let stream = this.fsw._readdirp(directory, {
fileFilter: entry => wh.filterPath(entry),
directoryFilter: entry => wh.filterDir(entry),
depth: 0
}).on(STR_DATA, async (entry) => {
if (this.fsw.closed) {
stream = undefined;
return;
}
const item = entry.path;
let path = sysPath.join(directory, item);
current.add(item);
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
return;
}
if (this.fsw.closed) {
stream = undefined;
return;
}
// Files that present in current directory snapshot
// but absent in previous are added to watch list and
// emit `add` event.
if (item === target || !target && !previous.has(item)) {
this.fsw._incrReadyCount();
// ensure relativeness of path is preserved in case of watcher reuse
path = sysPath.join(dir, sysPath.relative(dir, path));
this._addToNodeFs(path, initialAdd, wh, depth + 1);
}
}).on(EV_ERROR, this._boundHandleError);
return new Promise(resolve =>
stream.once(STR_END, () => {
if (this.fsw.closed) {
stream = undefined;
return;
}
const wasThrottled = throttler ? throttler.clear() : false;
resolve();
// Files that absent in current directory snapshot
// but present in previous emit `remove` event
// and are removed from @watched[directory].
previous.getChildren().filter((item) => {
return item !== directory &&
!current.has(item) &&
// in case of intersecting globs;
// a path may have been filtered out of this readdir, but
// shouldn't be removed because it matches a different glob
(!wh.hasGlob || wh.filterPath({
fullPath: sysPath.resolve(directory, item)
}));
}).forEach((item) => {
this.fsw._remove(directory, item);
});
stream = undefined;
// one more time for any missed in case changes came in extremely quickly
if (wasThrottled) this._handleRead(directory, false, wh, target, dir, depth, throttler);
})
);
}
/**
* Read directory to add / remove files from `@watched` list and re-read it on change.
* @param {String} dir fs path
* @param {fs.Stats} stats
* @param {Boolean} initialAdd
* @param {Number} depth relative to user-supplied path
* @param {String} target child path targeted for watch
* @param {Object} wh Common watch helpers for this path
* @param {String} realpath
* @returns {Promise<Function>} closer for the watcher instance.
*/
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) {
const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
const tracked = parentDir.has(sysPath.basename(dir));
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
if (!wh.hasGlob || wh.globFilter(dir)) this.fsw._emit(EV_ADD_DIR, dir, stats);
}
// ensure dir is tracked (harmless if redundant)
parentDir.add(sysPath.basename(dir));
this.fsw._getWatchedDir(dir);
let throttler;
let closer;
const oDepth = this.fsw.options.depth;
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) {
if (!target) {
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
if (this.fsw.closed) return;
}
closer = this._watchWithNodeFs(dir, (dirPath, stats) => {
// if current directory is removed, do nothing
if (stats && stats.mtimeMs === 0) return;
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
});
}
return closer;
}
/**
* Handle added file, directory, or glob pattern.
* Delegates call to _handleFile / _handleDir after checks.
* @param {String} path to file or ir
* @param {Boolean} initialAdd was the file added at watch instantiation?
* @param {Object} priorWh depth relative to user-supplied path
* @param {Number} depth Child path actually targeted for watch
* @param {String=} target Child path actually targeted for watch
* @returns {Promise}
*/
async _addToNodeFs(path, initialAdd, priorWh, depth, target) {
const ready = this.fsw._emitReady;
if (this.fsw._isIgnored(path) || this.fsw.closed) {
ready();
return false;
}
const wh = this.fsw._getWatchHelpers(path, depth);
if (!wh.hasGlob && priorWh) {
wh.hasGlob = priorWh.hasGlob;
wh.globFilter = priorWh.globFilter;
wh.filterPath = entry => priorWh.filterPath(entry);
wh.filterDir = entry => priorWh.filterDir(entry);
}
// evaluate what is at the path we're being asked to watch
try {
const stats = await statMethods[wh.statMethod](wh.watchPath);
if (this.fsw.closed) return;
if (this.fsw._isIgnored(wh.watchPath, stats)) {
ready();
return false;
}
const follow = this.fsw.options.followSymlinks && !path.includes(STAR) && !path.includes(BRACE_START);
let closer;
if (stats.isDirectory()) {
const absPath = sysPath.resolve(path);
const targetPath = follow ? await fsrealpath(path) : path;
if (this.fsw.closed) return;
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
if (this.fsw.closed) return;
// preserve this symlink's target path
if (absPath !== targetPath && targetPath !== undefined) {
this.fsw._symlinkPaths.set(absPath, targetPath);
}
} else if (stats.isSymbolicLink()) {
const targetPath = follow ? await fsrealpath(path) : path;
if (this.fsw.closed) return;
const parent = sysPath.dirname(wh.watchPath);
this.fsw._getWatchedDir(parent).add(wh.watchPath);
this.fsw._emit(EV_ADD, wh.watchPath, stats);
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
if (this.fsw.closed) return;
// preserve this symlink's target path
if (targetPath !== undefined) {
this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
}
} else {
closer = this._handleFile(wh.watchPath, stats, initialAdd);
}
ready();
this.fsw._addPathCloser(path, closer);
return false;
} catch (error) {
if (this.fsw._handleError(error)) {
ready();
return path;
}
}
}
}
module.exports = NodeFsHandler;

View File

@ -1,47 +1,51 @@
{
"name": "chokidar",
"description": "Minimal and efficient cross-platform file watching library",
"version": "3.6.0",
"version": "4.0.1",
"homepage": "https://github.com/paulmillr/chokidar",
"author": "Paul Miller (https://paulmillr.com)",
"contributors": [
"Paul Miller (https://paulmillr.com)",
"Elan Shanker"
],
"engines": {
"node": ">= 8.10.0"
},
"main": "index.js",
"types": "./types/index.d.ts",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"devDependencies": {
"@types/node": "^14",
"chai": "^4.3",
"dtslint": "^3.3.0",
"eslint": "^7.0.0",
"mocha": "^7.0.0",
"rimraf": "^3.0.0",
"sinon": "^9.0.1",
"sinon-chai": "^3.3.0",
"typescript": "^4.4.3",
"upath": "^1.2.0"
},
"files": [
"index.js",
"lib/*.js",
"types/index.d.ts"
"index.d.ts",
"index.d.ts.map",
"index.js.map",
"handler.js",
"handler.d.ts",
"handler.d.ts.map",
"handler.js.map",
"esm"
],
"main": "./index.js",
"module": "./esm/index.js",
"types": "./index.d.ts",
"exports": {
".": {
"import": "./esm/index.js",
"require": "./index.js"
},
"./handler.js": {
"import": "./esm/handler.js",
"require": "./handler.js"
}
},
"dependencies": {
"readdirp": "^4.0.1"
},
"devDependencies": {
"@paulmillr/jsbt": "0.2.1",
"@types/node": "20.14.8",
"chai": "4.3.4",
"prettier": "3.1.1",
"rimraf": "5.0.5",
"sinon": "12.0.1",
"sinon-chai": "3.7.0",
"typescript": "5.5.2",
"upath": "2.0.1"
},
"sideEffects": false,
"engines": {
"node": ">= 14.16.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/paulmillr/chokidar.git"
@ -51,11 +55,10 @@
},
"license": "MIT",
"scripts": {
"dtslint": "dtslint types",
"lint": "eslint --report-unused-disable-directives --ignore-path .gitignore .",
"build": "npm ls",
"mocha": "mocha --exit --timeout 90000",
"test": "npm run lint && npm run mocha"
"build": "tsc && tsc -p tsconfig.esm.json",
"lint": "prettier --check src",
"format": "prettier --write src",
"test": "node --test"
},
"keywords": [
"fs",

View File

@ -1,192 +0,0 @@
// TypeScript Version: 3.0
/// <reference types="node" />
import * as fs from "fs";
import { EventEmitter } from "events";
import { Matcher } from 'anymatch';
export class FSWatcher extends EventEmitter implements fs.FSWatcher {
options: WatchOptions;
/**
* Constructs a new FSWatcher instance with optional WatchOptions parameter.
*/
constructor(options?: WatchOptions);
/**
* Add files, directories, or glob patterns for tracking. Takes an array of strings or just one
* string.
*/
add(paths: string | ReadonlyArray<string>): this;
/**
* Stop watching files, directories, or glob patterns. Takes an array of strings or just one
* string.
*/
unwatch(paths: string | ReadonlyArray<string>): this;
/**
* Returns an object representing all the paths on the file system being watched by this
* `FSWatcher` instance. The object's keys are all the directories (using absolute paths unless
* the `cwd` option was used), and the values are arrays of the names of the items contained in
* each directory.
*/
getWatched(): {
[directory: string]: string[];
};
/**
* Removes all listeners from watched files.
*/
close(): Promise<void>;
on(event: 'add'|'addDir'|'change', listener: (path: string, stats?: fs.Stats) => void): this;
on(event: 'all', listener: (eventName: 'add'|'addDir'|'change'|'unlink'|'unlinkDir', path: string, stats?: fs.Stats) => void): this;
/**
* Error occurred
*/
on(event: 'error', listener: (error: Error) => void): this;
/**
* Exposes the native Node `fs.FSWatcher events`
*/
on(event: 'raw', listener: (eventName: string, path: string, details: any) => void): this;
/**
* Fires when the initial scan is complete
*/
on(event: 'ready', listener: () => void): this;
on(event: 'unlink'|'unlinkDir', listener: (path: string) => void): this;
on(event: string, listener: (...args: any[]) => void): this;
ref(): this;
unref(): this;
}
export interface WatchOptions {
/**
* Indicates whether the process should continue to run as long as files are being watched. If
* set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`,
* even if the process continues to run.
*/
persistent?: boolean;
/**
* ([anymatch](https://github.com/micromatch/anymatch)-compatible definition) Defines files/paths to
* be ignored. The whole relative or absolute path is tested, not just filename. If a function
* with two arguments is provided, it gets called twice per path - once with a single argument
* (the path), second time with two arguments (the path and the
* [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path).
*/
ignored?: Matcher;
/**
* If set to `false` then `add`/`addDir` events are also emitted for matching paths while
* instantiating the watching as chokidar discovers these file paths (before the `ready` event).
*/
ignoreInitial?: boolean;
/**
* When `false`, only the symlinks themselves will be watched for changes instead of following
* the link references and bubbling events through the link's path.
*/
followSymlinks?: boolean;
/**
* The base directory from which watch `paths` are to be derived. Paths emitted with events will
* be relative to this.
*/
cwd?: string;
/**
* If set to true then the strings passed to .watch() and .add() are treated as literal path
* names, even if they look like globs. Default: false.
*/
disableGlobbing?: boolean;
/**
* Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU
* utilization, consider setting this to `false`. It is typically necessary to **set this to
* `true` to successfully watch files over a network**, and it may be necessary to successfully
* watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides
* the `useFsEvents` default.
*/
usePolling?: boolean;
/**
* Whether to use the `fsevents` watching interface if available. When set to `true` explicitly
* and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on
* OS X, `usePolling: true` becomes the default.
*/
useFsEvents?: boolean;
/**
* If relying upon the [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object that
* may get passed with `add`, `addDir`, and `change` events, set this to `true` to ensure it is
* provided even in cases where it wasn't already available from the underlying watch events.
*/
alwaysStat?: boolean;
/**
* If set, limits how many levels of subdirectories will be traversed.
*/
depth?: number;
/**
* Interval of file system polling.
*/
interval?: number;
/**
* Interval of file system polling for binary files. ([see list of binary extensions](https://gi
* thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
*/
binaryInterval?: number;
/**
* Indicates whether to watch files that don't have read permissions if possible. If watching
* fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed
* silently.
*/
ignorePermissionErrors?: boolean;
/**
* `true` if `useFsEvents` and `usePolling` are `false`). Automatically filters out artifacts
* that occur when using editors that use "atomic writes" instead of writing directly to the
* source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change`
* event rather than `unlink` then `add`. If the default of 100 ms does not work well for you,
* you can override it by setting `atomic` to a custom value, in milliseconds.
*/
atomic?: boolean | number;
/**
* can be set to an object in order to adjust timing params:
*/
awaitWriteFinish?: AwaitWriteFinishOptions | boolean;
}
export interface AwaitWriteFinishOptions {
/**
* Amount of time in milliseconds for a file size to remain constant before emitting its event.
*/
stabilityThreshold?: number;
/**
* File size polling interval.
*/
pollInterval?: number;
}
/**
* produces an instance of `FSWatcher`.
*/
export function watch(
paths: string | ReadonlyArray<string>,
options?: WatchOptions
): FSWatcher;

View File

@ -0,0 +1,7 @@
.nyc_output
.travis.yml
coverage
test.js
node_modules
/.circleci
/tests/integration

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,78 @@
# detect-libc
Node.js module to detect the C standard library (libc) implementation
family and version in use on a given Linux system.
Provides a value suitable for use with the `LIBC` option of
[prebuild](https://www.npmjs.com/package/prebuild),
[prebuild-ci](https://www.npmjs.com/package/prebuild-ci) and
[prebuild-install](https://www.npmjs.com/package/prebuild-install),
therefore allowing build and provision of pre-compiled binaries
for musl-based Linux e.g. Alpine as well as glibc-based.
Currently supports libc detection of `glibc` and `musl`.
## Install
```sh
npm install detect-libc
```
## Usage
### API
```js
const { GLIBC, MUSL, family, version, isNonGlibcLinux } = require('detect-libc');
```
* `GLIBC` is a String containing the value "glibc" for comparison with `family`.
* `MUSL` is a String containing the value "musl" for comparison with `family`.
* `family` is a String representing the system libc family.
* `version` is a String representing the system libc version number.
* `isNonGlibcLinux` is a Boolean representing whether the system is a non-glibc Linux, e.g. Alpine.
### detect-libc command line tool
When run on a Linux system with a non-glibc libc,
the child command will be run with the `LIBC` environment variable
set to the relevant value.
On all other platforms will run the child command as-is.
The command line feature requires `spawnSync` provided by Node v0.12+.
```sh
detect-libc child-command
```
## Integrating with prebuild
```json
"scripts": {
"install": "detect-libc prebuild-install || node-gyp rebuild",
"test": "mocha && detect-libc prebuild-ci"
},
"dependencies": {
"detect-libc": "^1.0.2",
"prebuild-install": "^2.2.0"
},
"devDependencies": {
"prebuild": "^6.2.1",
"prebuild-ci": "^2.2.3"
}
```
## Licence
Copyright 2017 Lovell Fuller
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,18 @@
#!/usr/bin/env node
'use strict';
var spawnSync = require('child_process').spawnSync;
var libc = require('../');
var spawnOptions = {
env: process.env,
shell: true,
stdio: 'inherit'
};
if (libc.isNonGlibcLinux) {
spawnOptions.env.LIBC = process.env.LIBC || libc.family;
}
process.exit(spawnSync(process.argv[2], process.argv.slice(3), spawnOptions).status);

View File

@ -0,0 +1,92 @@
'use strict';
var platform = require('os').platform();
var spawnSync = require('child_process').spawnSync;
var readdirSync = require('fs').readdirSync;
var GLIBC = 'glibc';
var MUSL = 'musl';
var spawnOptions = {
encoding: 'utf8',
env: process.env
};
if (!spawnSync) {
spawnSync = function () {
return { status: 126, stdout: '', stderr: '' };
};
}
function contains (needle) {
return function (haystack) {
return haystack.indexOf(needle) !== -1;
};
}
function versionFromMuslLdd (out) {
return out.split(/[\r\n]+/)[1].trim().split(/\s/)[1];
}
function safeReaddirSync (path) {
try {
return readdirSync(path);
} catch (e) {}
return [];
}
var family = '';
var version = '';
var method = '';
if (platform === 'linux') {
// Try getconf
var glibc = spawnSync('getconf', ['GNU_LIBC_VERSION'], spawnOptions);
if (glibc.status === 0) {
family = GLIBC;
version = glibc.stdout.trim().split(' ')[1];
method = 'getconf';
} else {
// Try ldd
var ldd = spawnSync('ldd', ['--version'], spawnOptions);
if (ldd.status === 0 && ldd.stdout.indexOf(MUSL) !== -1) {
family = MUSL;
version = versionFromMuslLdd(ldd.stdout);
method = 'ldd';
} else if (ldd.status === 1 && ldd.stderr.indexOf(MUSL) !== -1) {
family = MUSL;
version = versionFromMuslLdd(ldd.stderr);
method = 'ldd';
} else {
// Try filesystem (family only)
var lib = safeReaddirSync('/lib');
if (lib.some(contains('-linux-gnu'))) {
family = GLIBC;
method = 'filesystem';
} else if (lib.some(contains('libc.musl-'))) {
family = MUSL;
method = 'filesystem';
} else if (lib.some(contains('ld-musl-'))) {
family = MUSL;
method = 'filesystem';
} else {
var usrSbin = safeReaddirSync('/usr/sbin');
if (usrSbin.some(contains('glibc'))) {
family = GLIBC;
method = 'filesystem';
}
}
}
}
}
var isNonGlibcLinux = (family !== '' && family !== GLIBC);
module.exports = {
GLIBC: GLIBC,
MUSL: MUSL,
family: family,
version: version,
method: method,
isNonGlibcLinux: isNonGlibcLinux
};

View File

@ -0,0 +1,35 @@
{
"name": "detect-libc",
"version": "1.0.3",
"description": "Node.js module to detect the C standard library (libc) implementation family and version",
"main": "lib/detect-libc.js",
"bin": {
"detect-libc": "./bin/detect-libc.js"
},
"scripts": {
"test": "semistandard && nyc --reporter=lcov ava"
},
"repository": {
"type": "git",
"url": "git://github.com/lovell/detect-libc"
},
"keywords": [
"libc",
"glibc",
"musl"
],
"author": "Lovell Fuller <npm@lovell.info>",
"contributors": [
"Niklas Salmoukas <niklas@salmoukas.com>"
],
"license": "Apache-2.0",
"devDependencies": {
"ava": "^0.23.0",
"nyc": "^11.3.0",
"proxyquire": "^1.8.0",
"semistandard": "^11.0.0"
},
"engines": {
"node": ">=0.10"
}
}

View File

@ -1,110 +0,0 @@
### [5.1.2](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2) (2021-03-06)
### Bug Fixes
* eliminate ReDoS ([#36](https://github.com/gulpjs/glob-parent/issues/36)) ([f923116](https://github.com/gulpjs/glob-parent/commit/f9231168b0041fea3f8f954b3cceb56269fc6366))
### [5.1.1](https://github.com/gulpjs/glob-parent/compare/v5.1.0...v5.1.1) (2021-01-27)
### Bug Fixes
* unescape exclamation mark ([#26](https://github.com/gulpjs/glob-parent/issues/26)) ([a98874f](https://github.com/gulpjs/glob-parent/commit/a98874f1a59e407f4fb1beb0db4efa8392da60bb))
## [5.1.0](https://github.com/gulpjs/glob-parent/compare/v5.0.0...v5.1.0) (2021-01-27)
### Features
* add `flipBackslashes` option to disable auto conversion of slashes (closes [#24](https://github.com/gulpjs/glob-parent/issues/24)) ([#25](https://github.com/gulpjs/glob-parent/issues/25)) ([eecf91d](https://github.com/gulpjs/glob-parent/commit/eecf91d5e3834ed78aee39c4eaaae654d76b87b3))
## [5.0.0](https://github.com/gulpjs/glob-parent/compare/v4.0.0...v5.0.0) (2021-01-27)
### ⚠ BREAKING CHANGES
* Drop support for node <6 & bump dependencies
### Miscellaneous Chores
* Drop support for node <6 & bump dependencies ([896c0c0](https://github.com/gulpjs/glob-parent/commit/896c0c00b4e7362f60b96e7fc295ae929245255a))
## [4.0.0](https://github.com/gulpjs/glob-parent/compare/v3.1.0...v4.0.0) (2021-01-27)
### ⚠ BREAKING CHANGES
* question marks are valid path characters on Windows so avoid flagging as a glob when alone
* Update is-glob dependency
### Features
* hoist regexps and strings for performance gains ([4a80667](https://github.com/gulpjs/glob-parent/commit/4a80667c69355c76a572a5892b0f133c8e1f457e))
* question marks are valid path characters on Windows so avoid flagging as a glob when alone ([2a551dd](https://github.com/gulpjs/glob-parent/commit/2a551dd0dc3235e78bf3c94843d4107072d17841))
* Update is-glob dependency ([e41fcd8](https://github.com/gulpjs/glob-parent/commit/e41fcd895d1f7bc617dba45c9d935a7949b9c281))
## [3.1.0](https://github.com/gulpjs/glob-parent/compare/v3.0.1...v3.1.0) (2021-01-27)
### Features
* allow basic win32 backslash use ([272afa5](https://github.com/gulpjs/glob-parent/commit/272afa5fd070fc0f796386a5993d4ee4a846988b))
* handle extglobs (parentheses) containing separators ([7db1bdb](https://github.com/gulpjs/glob-parent/commit/7db1bdb0756e55fd14619e8ce31aa31b17b117fd))
* new approach to braces/brackets handling ([8269bd8](https://github.com/gulpjs/glob-parent/commit/8269bd89290d99fac9395a354fb56fdcdb80f0be))
* pre-process braces/brackets sections ([9ef8a87](https://github.com/gulpjs/glob-parent/commit/9ef8a87f66b1a43d0591e7a8e4fc5a18415ee388))
* preserve escaped brace/bracket at end of string ([8cfb0ba](https://github.com/gulpjs/glob-parent/commit/8cfb0ba84202d51571340dcbaf61b79d16a26c76))
### Bug Fixes
* trailing escaped square brackets ([99ec9fe](https://github.com/gulpjs/glob-parent/commit/99ec9fecc60ee488ded20a94dd4f18b4f55c4ccf))
### [3.0.1](https://github.com/gulpjs/glob-parent/compare/v3.0.0...v3.0.1) (2021-01-27)
### Features
* use path-dirname ponyfill ([cdbea5f](https://github.com/gulpjs/glob-parent/commit/cdbea5f32a58a54e001a75ddd7c0fccd4776aacc))
### Bug Fixes
* unescape glob-escaped dirnames on output ([598c533](https://github.com/gulpjs/glob-parent/commit/598c533bdf49c1428bc063aa9b8db40c5a86b030))
## [3.0.0](https://github.com/gulpjs/glob-parent/compare/v2.0.0...v3.0.0) (2021-01-27)
### ⚠ BREAKING CHANGES
* update is-glob dependency
### Features
* update is-glob dependency ([5c5f8ef](https://github.com/gulpjs/glob-parent/commit/5c5f8efcee362a8e7638cf8220666acd8784f6bd))
## [2.0.0](https://github.com/gulpjs/glob-parent/compare/v1.3.0...v2.0.0) (2021-01-27)
### Features
* move up to dirname regardless of glob characters ([f97fb83](https://github.com/gulpjs/glob-parent/commit/f97fb83be2e0a9fc8d3b760e789d2ecadd6aa0c2))
## [1.3.0](https://github.com/gulpjs/glob-parent/compare/v1.2.0...v1.3.0) (2021-01-27)
## [1.2.0](https://github.com/gulpjs/glob-parent/compare/v1.1.0...v1.2.0) (2021-01-27)
### Reverts
* feat: make regex test strings smaller ([dc80fa9](https://github.com/gulpjs/glob-parent/commit/dc80fa9658dca20549cfeba44bbd37d5246fcce0))
## [1.1.0](https://github.com/gulpjs/glob-parent/compare/v1.0.0...v1.1.0) (2021-01-27)
### Features
* make regex test strings smaller ([cd83220](https://github.com/gulpjs/glob-parent/commit/cd832208638f45169f986d80fcf66e401f35d233))
## 1.0.0 (2021-01-27)

View File

@ -1,15 +0,0 @@
The ISC License
Copyright (c) 2015, 2019 Elan Shanker
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,137 +0,0 @@
<p align="center">
<a href="https://gulpjs.com">
<img height="257" width="114" src="https://raw.githubusercontent.com/gulpjs/artwork/master/gulp-2x.png">
</a>
</p>
# glob-parent
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Azure Pipelines Build Status][azure-pipelines-image]][azure-pipelines-url] [![Travis Build Status][travis-image]][travis-url] [![AppVeyor Build Status][appveyor-image]][appveyor-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Gitter chat][gitter-image]][gitter-url]
Extract the non-magic parent path from a glob string.
## Usage
```js
var globParent = require('glob-parent');
globParent('path/to/*.js'); // 'path/to'
globParent('/root/path/to/*.js'); // '/root/path/to'
globParent('/*.js'); // '/'
globParent('*.js'); // '.'
globParent('**/*.js'); // '.'
globParent('path/{to,from}'); // 'path'
globParent('path/!(to|from)'); // 'path'
globParent('path/?(to|from)'); // 'path'
globParent('path/+(to|from)'); // 'path'
globParent('path/*(to|from)'); // 'path'
globParent('path/@(to|from)'); // 'path'
globParent('path/**/*'); // 'path'
// if provided a non-glob path, returns the nearest dir
globParent('path/foo/bar.js'); // 'path/foo'
globParent('path/foo/'); // 'path/foo'
globParent('path/foo'); // 'path' (see issue #3 for details)
```
## API
### `globParent(maybeGlobString, [options])`
Takes a string and returns the part of the path before the glob begins. Be aware of Escaping rules and Limitations below.
#### options
```js
{
// Disables the automatic conversion of slashes for Windows
flipBackslashes: true
}
```
## Escaping
The following characters have special significance in glob patterns and must be escaped if you want them to be treated as regular path characters:
- `?` (question mark) unless used as a path segment alone
- `*` (asterisk)
- `|` (pipe)
- `(` (opening parenthesis)
- `)` (closing parenthesis)
- `{` (opening curly brace)
- `}` (closing curly brace)
- `[` (opening bracket)
- `]` (closing bracket)
**Example**
```js
globParent('foo/[bar]/') // 'foo'
globParent('foo/\\[bar]/') // 'foo/[bar]'
```
## Limitations
### Braces & Brackets
This library attempts a quick and imperfect method of determining which path
parts have glob magic without fully parsing/lexing the pattern. There are some
advanced use cases that can trip it up, such as nested braces where the outer
pair is escaped and the inner one contains a path separator. If you find
yourself in the unlikely circumstance of being affected by this or need to
ensure higher-fidelity glob handling in your library, it is recommended that you
pre-process your input with [expand-braces] and/or [expand-brackets].
### Windows
Backslashes are not valid path separators for globs. If a path with backslashes
is provided anyway, for simple cases, glob-parent will replace the path
separator for you and return the non-glob parent path (now with
forward-slashes, which are still valid as Windows path separators).
This cannot be used in conjunction with escape characters.
```js
// BAD
globParent('C:\\Program Files \\(x86\\)\\*.ext') // 'C:/Program Files /(x86/)'
// GOOD
globParent('C:/Program Files\\(x86\\)/*.ext') // 'C:/Program Files (x86)'
```
If you are using escape characters for a pattern without path parts (i.e.
relative to `cwd`), prefix with `./` to avoid confusing glob-parent.
```js
// BAD
globParent('foo \\[bar]') // 'foo '
globParent('foo \\[bar]*') // 'foo '
// GOOD
globParent('./foo \\[bar]') // 'foo [bar]'
globParent('./foo \\[bar]*') // '.'
```
## License
ISC
[expand-braces]: https://github.com/jonschlinkert/expand-braces
[expand-brackets]: https://github.com/jonschlinkert/expand-brackets
[downloads-image]: https://img.shields.io/npm/dm/glob-parent.svg
[npm-url]: https://www.npmjs.com/package/glob-parent
[npm-image]: https://img.shields.io/npm/v/glob-parent.svg
[azure-pipelines-url]: https://dev.azure.com/gulpjs/gulp/_build/latest?definitionId=2&branchName=master
[azure-pipelines-image]: https://dev.azure.com/gulpjs/gulp/_apis/build/status/glob-parent?branchName=master
[travis-url]: https://travis-ci.org/gulpjs/glob-parent
[travis-image]: https://img.shields.io/travis/gulpjs/glob-parent.svg?label=travis-ci
[appveyor-url]: https://ci.appveyor.com/project/gulpjs/glob-parent
[appveyor-image]: https://img.shields.io/appveyor/ci/gulpjs/glob-parent.svg?label=appveyor
[coveralls-url]: https://coveralls.io/r/gulpjs/glob-parent
[coveralls-image]: https://img.shields.io/coveralls/gulpjs/glob-parent/master.svg
[gitter-url]: https://gitter.im/gulpjs/gulp
[gitter-image]: https://badges.gitter.im/gulpjs/gulp.svg

View File

@ -1,42 +0,0 @@
'use strict';
var isGlob = require('is-glob');
var pathPosixDirname = require('path').posix.dirname;
var isWin32 = require('os').platform() === 'win32';
var slash = '/';
var backslash = /\\/g;
var enclosure = /[\{\[].*[\}\]]$/;
var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/;
var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g;
/**
* @param {string} str
* @param {Object} opts
* @param {boolean} [opts.flipBackslashes=true]
* @returns {string}
*/
module.exports = function globParent(str, opts) {
var options = Object.assign({ flipBackslashes: true }, opts);
// flip windows path separators
if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) {
str = str.replace(backslash, slash);
}
// special case for strings ending in enclosure containing path separator
if (enclosure.test(str)) {
str += slash;
}
// preserves full path in case of trailing path separator
str += 'a';
// remove path parts that are globby
do {
str = pathPosixDirname(str);
} while (isGlob(str) || globby.test(str));
// remove escape chars and return result
return str.replace(escaped, '$1');
};

View File

@ -1,48 +0,0 @@
{
"name": "glob-parent",
"version": "5.1.2",
"description": "Extract the non-magic parent path from a glob string.",
"author": "Gulp Team <team@gulpjs.com> (https://gulpjs.com/)",
"contributors": [
"Elan Shanker (https://github.com/es128)",
"Blaine Bublitz <blaine.bublitz@gmail.com>"
],
"repository": "gulpjs/glob-parent",
"license": "ISC",
"engines": {
"node": ">= 6"
},
"main": "index.js",
"files": [
"LICENSE",
"index.js"
],
"scripts": {
"lint": "eslint .",
"pretest": "npm run lint",
"test": "nyc mocha --async-only",
"azure-pipelines": "nyc mocha --async-only --reporter xunit -O output=test.xunit",
"coveralls": "nyc report --reporter=text-lcov | coveralls"
},
"dependencies": {
"is-glob": "^4.0.1"
},
"devDependencies": {
"coveralls": "^3.0.11",
"eslint": "^2.13.1",
"eslint-config-gulp": "^3.0.1",
"expect": "^1.20.2",
"mocha": "^6.0.2",
"nyc": "^13.3.0"
},
"keywords": [
"glob",
"parent",
"strip",
"path",
"dirname",
"directory",
"base",
"wildcard"
]
}

View File

@ -1437,16 +1437,16 @@ function sliceFactory(collection, begin, end, useKeys) {
return collection;
}
var resolvedBegin = resolveBegin(begin, originalSize);
var resolvedEnd = resolveEnd(end, originalSize);
// begin or end will be NaN if they were provided as negative numbers and
// begin or end can not be resolved if they were provided as negative numbers and
// this collection's size is unknown. In that case, cache first so there is
// a known size and these do not resolve to NaN.
if (resolvedBegin !== resolvedBegin || resolvedEnd !== resolvedEnd) {
if (typeof originalSize === 'undefined' && (begin < 0 || end < 0)) {
return sliceFactory(collection.toSeq().cacheResult(), begin, end, useKeys);
}
var resolvedBegin = resolveBegin(begin, originalSize);
var resolvedEnd = resolveEnd(end, originalSize);
// Note: resolvedEnd is undefined when the original sequence's length is
// unknown and this slice did not supply an end and should contain all
// elements after resolvedBegin.
@ -5922,7 +5922,7 @@ function defaultConverter(k, v) {
return isIndexed(v) ? v.toList() : isKeyed(v) ? v.toMap() : v.toSet();
}
var version = "4.3.6";
var version = "4.3.7";
var Immutable = {
version: version,

View File

@ -1443,16 +1443,16 @@
return collection;
}
var resolvedBegin = resolveBegin(begin, originalSize);
var resolvedEnd = resolveEnd(end, originalSize);
// begin or end will be NaN if they were provided as negative numbers and
// begin or end can not be resolved if they were provided as negative numbers and
// this collection's size is unknown. In that case, cache first so there is
// a known size and these do not resolve to NaN.
if (resolvedBegin !== resolvedBegin || resolvedEnd !== resolvedEnd) {
if (typeof originalSize === 'undefined' && (begin < 0 || end < 0)) {
return sliceFactory(collection.toSeq().cacheResult(), begin, end, useKeys);
}
var resolvedBegin = resolveBegin(begin, originalSize);
var resolvedEnd = resolveEnd(end, originalSize);
// Note: resolvedEnd is undefined when the original sequence's length is
// unknown and this slice did not supply an end and should contain all
// elements after resolvedBegin.
@ -5928,7 +5928,7 @@
return isIndexed(v) ? v.toList() : isKeyed(v) ? v.toMap() : v.toSet();
}
var version = "4.3.6";
var version = "4.3.7";
var Immutable = {
version: version,

View File

@ -28,7 +28,7 @@ if(e)return this.cacheResult().__iterate(t,e);var r,n=V(this._collection),i=0;if
var e;if(bt&&void 0!==(e=zt.get(t)))return e;if(void 0!==(e=t[Et]))return e;if(!wt){if(void 0!==(e=t.propertyIsEnumerable&&t.propertyIsEnumerable[Et]))return e;if(void 0!==(e=function(t){if(t&&0<t.nodeType)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}(t)))return e}if(e=St(),bt)zt.set(t,e);else{if(void 0!==mt&&!1===mt(t))throw Error("Non-extensible objects are not allowed as keys.");if(wt)Object.defineProperty(t,Et,{enumerable:!1,configurable:!1,writable:!1,value:e});else if(void 0!==t.propertyIsEnumerable&&t.propertyIsEnumerable===t.constructor.prototype.propertyIsEnumerable)t.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},t.propertyIsEnumerable[Et]=e;else{if(void 0===t.nodeType)throw Error("Unable to set a non-enumerable property on object.");t[Et]=e}}return e}(r);case"symbol":return void 0===(e=It[t=r])?(e=St(),It[t]=e):e;default:if("function"==typeof r.toString)return gt(""+r);throw Error("Value type "+typeof r+" cannot be hashed.")}}function dt(t){return null===t?1108378658:1108378659}function gt(t){for(var e=0,r=0;r<t.length;r++)e=31*e+t.charCodeAt(r)|0;return lt(e)}var mt=Object.isExtensible,wt=function(){try{return Object.defineProperty({},"@",{}),!0}catch(t){return!1}}();function St(){var t=++Ot;return 1073741824&Ot&&(Ot=0),t}var zt,bt="function"==typeof WeakMap;bt&&(zt=new WeakMap);var It=Object.create(null),Ot=0,Et="__immutablehash__";"function"==typeof Symbol&&(Et=Symbol(Et));var jt=16,qt=255,Mt=0,Dt={},xt=function(t){function e(t,e){this._iter=t,this._useKeys=e,this.size=t.size}return t&&(e.__proto__=t),((e.prototype=Object.create(t&&t.prototype)).constructor=e).prototype.get=function(t,e){return this._iter.get(t,e)},e.prototype.has=function(t){return this._iter.has(t)},e.prototype.valueSeq=function(){return this._iter.valueSeq()},e.prototype.reverse=function(){var t=this,e=Kt(this,!0);return this._useKeys||(e.valueSeq=function(){return t._iter.toSeq().reverse()}),e},
e.prototype.map=function(t,e){var r=this,n=Tt(this,t,e);return this._useKeys||(n.valueSeq=function(){return r._iter.toSeq().map(t,e)}),n},e.prototype.__iterate=function(r,t){var n=this;return this._iter.__iterate(function(t,e){return r(t,e,n)},t)},e.prototype.__iterator=function(t,e){return this._iter.__iterator(t,e)},e}(G);xt.prototype[k]=!0;var At=function(t){function e(t){this._iter=t,this.size=t.size}return t&&(e.__proto__=t),((e.prototype=Object.create(t&&t.prototype)).constructor=e).prototype.includes=function(t){return this._iter.includes(t)},e.prototype.__iterate=function(e,r){var n=this,i=0;return r&&c(this),this._iter.__iterate(function(t){return e(t,r?n.size-++i:i++,n)},r)},e.prototype.__iterator=function(e,r){var n=this,i=this._iter.__iterator(T,r),o=0;return r&&c(this),new P(function(){var t=i.next();return t.done?t:W(e,r?n.size-++o:o++,t.value,t)})},e}(Z),kt=function(t){function e(t){this._iter=t,this.size=t.size}return t&&(e.__proto__=t),((e.prototype=Object.create(t&&t.prototype)).constructor=e).prototype.has=function(t){return this._iter.includes(t)},e.prototype.__iterate=function(e,t){var r=this;return this._iter.__iterate(function(t){return e(t,t,r)},t)},e.prototype.__iterator=function(e,t){var r=this._iter.__iterator(T,t);return new P(function(){var t=r.next();return t.done?t:W(e,t.value,t.value,t)})},e}($),Rt=function(t){function e(t){this._iter=t,this.size=t.size}return t&&(e.__proto__=t),((e.prototype=Object.create(t&&t.prototype)).constructor=e).prototype.entrySeq=function(){return this._iter.toSeq()},e.prototype.__iterate=function(r,t){var n=this;return this._iter.__iterate(function(t){if(t){Yt(t);var e=f(t);return r(e?t.get(1):t[1],e?t.get(0):t[0],n)}},t)},e.prototype.__iterator=function(n,t){var i=this._iter.__iterator(T,t);return new P(function(){for(;;){var t=i.next();if(t.done)return t;var e=t.value;if(e){Yt(e);var r=f(e);return W(n,r?e.get(0):e[0],r?e.get(1):e[1],t)}}})},e}(G);function Ut(i){var t=Xt(i);return t._iter=i,t.size=i.size,t.flip=function(){return i},t.reverse=function(){
var t=i.reverse.apply(this);return t.flip=function(){return i.reverse()},t},t.has=function(t){return i.includes(t)},t.includes=function(t){return i.has(t)},t.cacheResult=Ft,t.__iterateUncached=function(r,t){var n=this;return i.__iterate(function(t,e){return!1!==r(e,t,n)},t)},t.__iteratorUncached=function(t,e){if(t!==K)return i.__iterator(t===T?U:T,e);var r=i.__iterator(t,e);return new P(function(){var t,e=r.next();return e.done||(t=e.value[0],e.value[0]=e.value[1],e.value[1]=t),e})},t}function Tt(o,u,s){var t=Xt(o);return t.size=o.size,t.has=function(t){return o.has(t)},t.get=function(t,e){var r=o.get(t,v);return r===v?e:u.call(s,r,t,o)},t.__iterateUncached=function(n,t){var i=this;return o.__iterate(function(t,e,r){return!1!==n(u.call(s,t,e,r),e,i)},t)},t.__iteratorUncached=function(n,t){var i=o.__iterator(K,t);return new P(function(){var t=i.next();if(t.done)return t;var e=t.value,r=e[0];return W(n,r,u.call(s,e[1],r,o),t)})},t}function Kt(u,s){var a=this,t=Xt(u);return t._iter=u,t.size=u.size,t.reverse=function(){return u},u.flip&&(t.flip=function(){var t=Ut(u);return t.reverse=function(){return u.flip()},t}),t.get=function(t,e){return u.get(s?t:-1-t,e)},t.has=function(t){return u.has(s?t:-1-t)},t.includes=function(t){return u.includes(t)},t.cacheResult=Ft,t.__iterate=function(r,n){var i=this,o=0;return n&&c(u),u.__iterate(function(t,e){return r(t,s?e:n?i.size-++o:o++,i)},!n)},t.__iterator=function(r,n){var i=0;n&&c(u);var o=u.__iterator(K,!n);return new P(function(){var t=o.next();if(t.done)return t;var e=t.value;return W(r,s?e[0]:n?a.size-++i:i++,e[1],t)})},t}function Lt(u,s,a,c){var t=Xt(u);return c&&(t.has=function(t){var e=u.get(t,v);return e!==v&&!!s.call(a,e,t,u)},t.get=function(t,e){var r=u.get(t,v);return r!==v&&s.call(a,r,t,u)?r:e}),t.__iterateUncached=function(n,t){var i=this,o=0;return u.__iterate(function(t,e,r){if(s.call(a,t,e,r))return o++,n(t,c?e:o-1,i)},t),o},t.__iteratorUncached=function(n,t){var i=u.__iterator(K,t),o=0;return new P(function(){for(;;){var t=i.next();if(t.done)return t
;var e=t.value,r=e[0],e=e[1];if(s.call(a,e,r,u))return W(n,c?r:o++,e,t)}})},t}function Ct(s,t,e,a){var r=s.size;if(p(t,e,r))return s;var c=y(t,r),r=w(e,r);if(c!=c||r!=r)return Ct(s.toSeq().cacheResult(),t,e,a);var f,r=r-c;r==r&&(f=r<0?0:r);r=Xt(s);return r.size=0===f?f:s.size&&f||void 0,!a&&M(s)&&0<=f&&(r.get=function(t,e){return 0<=(t=h(this,t))&&t<f?s.get(t+c,e):e}),r.__iterateUncached=function(r,t){var n=this;if(0===f)return 0;if(t)return this.cacheResult().__iterate(r,t);var i=0,o=!0,u=0;return s.__iterate(function(t,e){if(!(o=o&&i++<c))return u++,!1!==r(t,a?e:u-1,n)&&u!==f}),u},r.__iteratorUncached=function(e,t){if(0!==f&&t)return this.cacheResult().__iterator(e,t);if(0===f)return new P(N);var r=s.__iterator(e,t),n=0,i=0;return new P(function(){for(;n++<c;)r.next();if(++i>f)return N();var t=r.next();return a||e===T||t.done?t:W(e,i-1,e===U?void 0:t.value[1],t)})},r}function Bt(e,c,f,h){var t=Xt(e);return t.__iterateUncached=function(n,t){var i=this;if(t)return this.cacheResult().__iterate(n,t);var o=!0,u=0;return e.__iterate(function(t,e,r){if(!(o=o&&c.call(f,t,e,r)))return u++,n(t,h?e:u-1,i)}),u},t.__iteratorUncached=function(i,t){var o=this;if(t)return this.cacheResult().__iterator(i,t);var u=e.__iterator(K,t),s=!0,a=0;return new P(function(){var t;do{if((t=u.next()).done)return h||i===T?t:W(i,a++,i===U?void 0:t.value[1],t);var e=t.value,r=e[0],n=e[1];s=s&&c.call(f,n,r,o)}while(s);return i===K?t:W(i,r,n,t)})},t}function Pt(t,s,a){var c=Xt(t);return c.__iterateUncached=function(i,e){if(e)return this.cacheResult().__iterate(i,e);var o=0,u=!1;return function r(t,n){t.__iterate(function(t,e){return(!s||n<s)&&f(t)?r(t,n+1):(o++,!1===i(t,a?e:o-1,c)&&(u=!0)),!u},e)}(t,0),o},c.__iteratorUncached=function(r,n){if(n)return this.cacheResult().__iterator(r,n);var i=t.__iterator(r,n),o=[],u=0;return new P(function(){for(;i;){var t=i.next();if(!1===t.done){var e=t.value;if(r===K&&(e=e[1]),s&&!(o.length<s)||!f(e))return a?t:W(r,u++,e,t);o.push(i),i=e.__iterator(r,n)}else i=o.pop()}return N()})},c}function Wt(r,n,i){
;var e=t.value,r=e[0],e=e[1];if(s.call(a,e,r,u))return W(n,c?r:o++,e,t)}})},t}function Ct(s,t,e,a){var r=s.size;if(p(t,e,r))return s;if(void 0===r&&(t<0||e<0))return Ct(s.toSeq().cacheResult(),t,e,a);var c,f=y(t,r),r=w(e,r)-f;r==r&&(c=r<0?0:r);r=Xt(s);return r.size=0===c?c:s.size&&c||void 0,!a&&M(s)&&0<=c&&(r.get=function(t,e){return 0<=(t=h(this,t))&&t<c?s.get(t+f,e):e}),r.__iterateUncached=function(r,t){var n=this;if(0===c)return 0;if(t)return this.cacheResult().__iterate(r,t);var i=0,o=!0,u=0;return s.__iterate(function(t,e){if(!(o=o&&i++<f))return u++,!1!==r(t,a?e:u-1,n)&&u!==c}),u},r.__iteratorUncached=function(e,t){if(0!==c&&t)return this.cacheResult().__iterator(e,t);if(0===c)return new P(N);var r=s.__iterator(e,t),n=0,i=0;return new P(function(){for(;n++<f;)r.next();if(++i>c)return N();var t=r.next();return a||e===T||t.done?t:W(e,i-1,e===U?void 0:t.value[1],t)})},r}function Bt(e,c,f,h){var t=Xt(e);return t.__iterateUncached=function(n,t){var i=this;if(t)return this.cacheResult().__iterate(n,t);var o=!0,u=0;return e.__iterate(function(t,e,r){if(!(o=o&&c.call(f,t,e,r)))return u++,n(t,h?e:u-1,i)}),u},t.__iteratorUncached=function(i,t){var o=this;if(t)return this.cacheResult().__iterator(i,t);var u=e.__iterator(K,t),s=!0,a=0;return new P(function(){var t;do{if((t=u.next()).done)return h||i===T?t:W(i,a++,i===U?void 0:t.value[1],t);var e=t.value,r=e[0],n=e[1];s=s&&c.call(f,n,r,o)}while(s);return i===K?t:W(i,r,n,t)})},t}function Pt(t,s,a){var c=Xt(t);return c.__iterateUncached=function(i,e){if(e)return this.cacheResult().__iterate(i,e);var o=0,u=!1;return function r(t,n){t.__iterate(function(t,e){return(!s||n<s)&&f(t)?r(t,n+1):(o++,!1===i(t,a?e:o-1,c)&&(u=!0)),!u},e)}(t,0),o},c.__iteratorUncached=function(r,n){if(n)return this.cacheResult().__iterator(r,n);var i=t.__iterator(r,n),o=[],u=0;return new P(function(){for(;i;){var t=i.next();if(!1===t.done){var e=t.value;if(r===K&&(e=e[1]),s&&!(o.length<s)||!f(e))return a?t:W(r,u++,e,t);o.push(i),i=e.__iterator(r,n)}else i=o.pop()}return N()})},c}function Wt(r,n,i){
n=n||Gt;var t=a(r),o=0,u=r.toSeq().map(function(t,e){return[e,t,o++,i?i(t,e,r):t]}).valueSeq().toArray();return u.sort(function(t,e){return n(t[3],e[3])||t[2]-e[2]}).forEach(t?function(t,e){u[e].length=2}:function(t,e){u[e]=t[1]}),(t?G:z(r)?Z:$)(u)}function Nt(r,n,i){if(n=n||Gt,i){var t=r.toSeq().map(function(t,e){return[t,i(t,e,r)]}).reduce(function(t,e){return Ht(n,t[1],e[1])?e:t});return t&&t[0]}return r.reduce(function(t,e){return Ht(n,t,e)?e:t})}function Ht(t,e,r){t=t(r,e);return 0===t&&r!==e&&(null==r||r!=r)||0<t}function Jt(t,u,s,a){var e=Xt(t),t=new tt(s).map(function(t){return t.size});return e.size=a?t.max():t.min(),e.__iterate=function(t,e){for(var r,n=this.__iterator(T,e),i=0;!(r=n.next()).done&&!1!==t(r.value,i++,this););return i},e.__iteratorUncached=function(e,r){var n=s.map(function(t){return t=I(t),V(r?t.reverse():t)}),i=0,o=!1;return new P(function(){var t;return o||(t=n.map(function(t){return t.next()}),o=a?t.every(function(t){return t.done}):t.some(function(t){return t.done})),o?N():W(e,i++,u.apply(null,t.map(function(t){return t.value})))})},e}function Vt(t,e){return t===e?t:M(t)?e:t.constructor(e)}function Yt(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function Qt(t){return a(t)?O:z(t)?E:j}function Xt(t){return Object.create((a(t)?G:z(t)?Z:$).prototype)}function Ft(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):F.prototype.cacheResult.call(this)}function Gt(t,e){return void 0===t&&void 0===e?0:void 0===t?1:void 0===e?-1:e<t?1:t<e?-1:0}function Zt(t,e){for(var r=Math.max(0,t.length-(e=e||0)),n=Array(r),i=0;i<r;i++)n[i]=t[i+e];return n}function $t(t,e){if(!t)throw Error(e)}function te(t){$t(t!==1/0,"Cannot perform this action with an infinite size.")}function ee(t){if(X(t)&&"string"!=typeof t)return t;if(R(t))return t.toArray();throw new TypeError("Invalid keyPath: expected Ordered Collection or Array: "+t)}At.prototype.cacheResult=xt.prototype.cacheResult=kt.prototype.cacheResult=Rt.prototype.cacheResult=Ft
;var re=Object.prototype.toString;function ne(t){if(!t||"object"!=typeof t||"[object Object]"!==re.call(t))return!1;t=Object.getPrototypeOf(t);if(null===t)return!0;for(var e=t,r=Object.getPrototypeOf(t);null!==r;)r=Object.getPrototypeOf(e=r);return e===t}function ie(t){return"object"==typeof t&&(A(t)||Array.isArray(t)||ne(t))}function oe(e){try{return"string"==typeof e?JSON.stringify(e):e+""}catch(t){return JSON.stringify(e)}}function ue(t,e){return A(t)?t.has(e):ie(t)&&Q.call(t,e)}function se(t,e,r){return A(t)?t.get(e,r):ue(t,e)?"function"==typeof t.get?t.get(e):t[e]:r}function ae(t){if(Array.isArray(t))return Zt(t);var e,r={};for(e in t)Q.call(t,e)&&(r[e]=t[e]);return r}function ce(t,e){if(!ie(t))throw new TypeError("Cannot update non-data-structure value: "+t);if(A(t)){if(!t.remove)throw new TypeError("Cannot update immutable value without .remove() method: "+t);return t.remove(e)}if(!Q.call(t,e))return t;t=ae(t);return Array.isArray(t)?t.splice(e,1):delete t[e],t}function fe(t,e,r){if(!ie(t))throw new TypeError("Cannot update non-data-structure value: "+t);if(A(t)){if(!t.set)throw new TypeError("Cannot update immutable value without .set() method: "+t);return t.set(e,r)}if(Q.call(t,e)&&r===t[e])return t;t=ae(t);return t[e]=r,t}function he(t,e,r,n){n||(n=r,r=void 0);n=function t(e,r,n,i,o,u){var s=r===v;if(i===n.length){var a=s?o:r,c=u(a);return c===a?r:c}if(!s&&!ie(r))throw new TypeError("Cannot update within non-data-structure value in path ["+n.slice(0,i).map(oe)+"]: "+r);var a=n[i];var c=s?v:se(r,a,v);var u=t(c===v?e:A(c),c,n,i+1,o,u);return u===c?r:u===v?ce(r,a):fe(s?e?Qe():{}:r,a,u)}(A(t),t,ee(e),0,r,n);return n===v?r:n}function _e(t,e,r){return he(t,e,v,function(){return r})}function pe(t,e){return _e(this,t,e)}function le(t,e){return he(t,e,function(){return v})}function ve(t){return le(this,t)}function ye(t,e,r,n){return he(t,[e],r,n)}function de(t,e,r){return 1===arguments.length?t(this):ye(this,t,e,r)}function ge(t,e,r){return he(this,t,e,r)}function me(){for(var t=[],e=arguments.length;e--;
)t[e]=arguments[e];return Se(this,t)}function we(t){for(var e=[],r=arguments.length-1;0<r--;)e[r]=arguments[r+1];if("function"!=typeof t)throw new TypeError("Invalid merger function: "+t);return Se(this,e,t)}function Se(t,e,i){for(var r=[],n=0;n<e.length;n++){var o=O(e[n]);0!==o.size&&r.push(o)}return 0===r.length?t:0!==t.toSeq().size||t.__ownerID||1!==r.length?t.withMutations(function(n){for(var t=i?function(e,r){ye(n,r,v,function(t){return t===v?e:i(t,e,r)})}:function(t,e){n.set(e,t)},e=0;e<r.length;e++)r[e].forEach(t)}):t.constructor(r[0])}function ze(t){for(var e=[],r=arguments.length-1;0<r--;)e[r]=arguments[r+1];return je(t,e)}function be(t,e){for(var r=[],n=arguments.length-2;0<n--;)r[n]=arguments[n+2];return je(e,r,t)}function Ie(t){for(var e=[],r=arguments.length-1;0<r--;)e[r]=arguments[r+1];return Ee(t,e)}function Oe(t,e){for(var r=[],n=arguments.length-2;0<n--;)r[n]=arguments[n+2];return Ee(e,r,t)}function Ee(t,e,r){return je(t,e,(i=r,function t(e,r,n){return ie(e)&&ie(r)&&function(t,e){return t=F(t),e=F(e),z(t)===z(e)&&a(t)===a(e)}(e,r)?je(e,[r],t):i?i(e,r,n):r}));var i}function je(n,t,i){if(!ie(n))throw new TypeError("Cannot merge into non-data-structure value: "+n);if(A(n))return"function"==typeof i&&n.mergeWith?n.mergeWith.apply(n,[i].concat(t)):(n.merge?n.merge:n.concat).apply(n,t);for(var e=Array.isArray(n),o=n,r=e?E:O,u=e?function(t){o===n&&(o=ae(o)),o.push(t)}:function(t,e){var r=Q.call(o,e),t=r&&i?i(o[e],t,e):t;r&&t===o[e]||(o===n&&(o=ae(o)),o[e]=t)},s=0;s<t.length;s++)r(t[s]).forEach(u);return o}function qe(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return Ee(this,t)}function Me(t){for(var e=[],r=arguments.length-1;0<r--;)e[r]=arguments[r+1];return Ee(this,e,t)}function De(t){for(var e=[],r=arguments.length-1;0<r--;)e[r]=arguments[r+1];return he(this,t,Qe(),function(t){return je(t,e)})}function xe(t){for(var e=[],r=arguments.length-1;0<r--;)e[r]=arguments[r+1];return he(this,t,Qe(),function(t){return Ee(t,e)})}function Ae(t){var e=this.asMutable();return t(e),e.wasAltered(
@ -51,5 +51,5 @@ return this.toKeyedSeq().reverse().findEntry(t,e,r)},findLastKey:function(t,e){r
var r=arguments.length;if(e=Math.max(e||0,0),0===r||2===r&&!e)return this;t=y(t,t<0?this.count():this.size);var n=this.slice(0,t);return Vt(this,1===r?n:n.concat(Zt(arguments,2),this.slice(t+e)))},findLastIndex:function(t,e){e=this.findLastEntry(t,e);return e?e[0]:-1},first:function(t){return this.get(0,t)},flatten:function(t){return Vt(this,Pt(this,t,!1))},get:function(r,t){return(r=h(this,r))<0||this.size===1/0||void 0!==this.size&&this.size<r?t:this.find(function(t,e){return e===r},void 0,t)},has:function(t){return 0<=(t=h(this,t))&&(void 0!==this.size?this.size===1/0||t<this.size:!!~this.indexOf(t))},interpose:function(t){return Vt(this,(u=t,(t=Xt(o=this)).size=o.size&&2*o.size-1,t.__iterateUncached=function(e,t){var r=this,n=0;return o.__iterate(function(t){return(!n||!1!==e(u,n++,r))&&!1!==e(t,n++,r)},t),n},t.__iteratorUncached=function(t,e){var r,n=o.__iterator(T,e),i=0;return new P(function(){return(!r||i%2)&&(r=n.next()).done?r:i%2?W(t,i++,u):W(t,i++,r.value,r)})},t));var o,u},interleave:function(){var t=[this].concat(Zt(arguments)),e=Jt(this.toSeq(),Z.of,t),r=e.flatten(!0);return e.size&&(r.size=e.size*t.length),Vt(this,r)},keySeq:function(){return Hr(0,this.size)},last:function(t){return this.get(-1,t)},skipWhile:function(t,e){return Vt(this,Bt(this,t,e,!1))},zip:function(){var t=[this].concat(Zt(arguments));return Vt(this,Jt(this,on,t))},zipAll:function(){var t=[this].concat(Zt(arguments));return Vt(this,Jt(this,on,t,!0))},zipWith:function(t){var e=Zt(arguments);return Vt(e[0]=this,Jt(this,t,e))}});var Gr=E.prototype;Gr[S]=!0,Gr[k]=!0,Ur(j,{get:function(t,e){return this.has(t)?t:e},includes:function(t){return this.has(t)},keySeq:function(){return this.valueSeq()}});var Zr=j.prototype;function $r(t,n,i,o,u,e){return te(t.size),t.__iterate(function(t,e,r){i=u?(u=!1,t):n.call(o,i,t,e,r)},e),i}function tn(t,e){return e}function en(t,e){return[e,t]}function rn(t){return function(){return!t.apply(this,arguments)}}function nn(t){return function(){return-t.apply(this,arguments)}}function on(){return Zt(
arguments)}function un(t,e){return t<e?1:e<t?-1:0}function sn(t,e){return t^e+2654435769+(t<<6)+(t>>2)|0}Zr.has=Xr.includes,Zr.contains=Zr.includes,Zr.keys=Zr.values,Ur(G,Fr),Ur(Z,Gr),Ur($,Zr);var an=function(t){function e(r){return null==r?_n():kr(r)?r:_n().withMutations(function(e){var t=j(r);te(t.size),t.forEach(function(t){return e.add(t)})})}return t&&(e.__proto__=t),((e.prototype=Object.create(t&&t.prototype)).constructor=e).of=function(){return this(arguments)},e.fromKeys=function(t){return this(O(t).keySeq())},e.prototype.toString=function(){return this.__toString("OrderedSet {","}")},e}(Kr);an.isOrderedSet=kr;var cn,fn=an.prototype;function hn(t,e){var r=Object.create(fn);return r.size=t?t.size:0,r._map=t,r.__ownerID=e,r}function _n(){return cn=cn||hn(zr())}fn[k]=!0,fn.zip=Gr.zip,fn.zipWith=Gr.zipWith,fn.zipAll=Gr.zipAll,fn.__empty=_n,fn.__make=hn;Zr={LeftThenRight:-1,RightThenLeft:1};Gr=function(u,s){var a;!function(t){if(x(t))throw Error("Can not call `Record` with an immutable Record as default values. Use a plain javascript object instead.");if(A(t))throw Error("Can not call `Record` with an immutable Collection as default values. Use a plain javascript object instead.");if(null===t||"object"!=typeof t)throw Error("Can not call `Record` with a non-object as default values. Use a plain javascript object instead.")}(u);var c=function(t){var n=this;if(t instanceof c)return t;if(!(this instanceof c))return new c(t);if(!a){a=!0;var e=Object.keys(u),r=f._indices={};f._name=s,f._keys=e,f._defaultValues=u;for(var i=0;i<e.length;i++){var o=e[i];r[o]=i,f[o]?"object"==typeof console&&console.warn&&console.warn("Cannot define "+vn(this)+' with property "'+o+'" since that property name is part of the Record API.'):function(t,e){try{Object.defineProperty(t,e,{get:function(){return this.get(e)},set:function(t){$t(this.__ownerID,"Cannot set on an immutable record."),this.set(e,t)}})}catch(t){}}(f,o)}}return this.__ownerID=void 0,this._values=ur().withMutations(function(r){r.setSize(n._keys.length),O(t).forEach(
function(t,e){r.set(n._indices[e],t===n._defaultValues[e]?void 0:t)})}),this},f=c.prototype=Object.create(pn);return f.constructor=c,s&&(c.displayName=s),c};Gr.prototype.toString=function(){for(var t,e=vn(this)+" { ",r=this._keys,n=0,i=r.length;n!==i;n++)e+=(n?", ":"")+(t=r[n])+": "+oe(this.get(t));return e+" }"},Gr.prototype.equals=function(t){return this===t||x(t)&&yn(this).equals(yn(t))},Gr.prototype.hashCode=function(){return yn(this).hashCode()},Gr.prototype.has=function(t){return this._indices.hasOwnProperty(t)},Gr.prototype.get=function(t,e){if(!this.has(t))return e;e=this._values.get(this._indices[t]);return void 0===e?this._defaultValues[t]:e},Gr.prototype.set=function(t,e){if(this.has(t)){e=this._values.set(this._indices[t],e===this._defaultValues[t]?void 0:e);if(e!==this._values&&!this.__ownerID)return ln(this,e)}return this},Gr.prototype.remove=function(t){return this.set(t)},Gr.prototype.clear=function(){var t=this._values.clear().setSize(this._keys.length);return this.__ownerID?this:ln(this,t)},Gr.prototype.wasAltered=function(){return this._values.wasAltered()},Gr.prototype.toSeq=function(){return yn(this)},Gr.prototype.toJS=function(){return Tr(this)},Gr.prototype.entries=function(){return this.__iterator(K)},Gr.prototype.__iterator=function(t,e){return yn(this).__iterator(t,e)},Gr.prototype.__iterate=function(t,e){return yn(this).__iterate(t,e)},Gr.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._values.__ensureOwner(t);return t?ln(this,e,t):(this.__ownerID=t,this._values=e,this)},Gr.isRecord=x,Gr.getDescriptiveName=vn;var pn=Gr.prototype;function ln(t,e,r){t=Object.create(Object.getPrototypeOf(t));return t._values=e,t.__ownerID=r,t}function vn(t){return t.constructor.displayName||t.constructor.name||"Record"}function yn(e){return ot(e._keys.map(function(t){return[t,e.get(t)]}))}pn[D]=!0,pn[e]=pn.remove,pn.deleteIn=pn.removeIn=ve,pn.getIn=Vr,pn.hasIn=Xr.hasIn,pn.merge=me,pn.mergeWith=we,pn.mergeIn=De,pn.mergeDeep=qe,pn.mergeDeepWith=Me,pn.mergeDeepIn=xe,
pn.setIn=pe,pn.update=de,pn.updateIn=ge,pn.withMutations=Ae,pn.asMutable=ke,pn.asImmutable=Re,pn[B]=pn.entries,pn.toJSON=pn.toObject=Xr.toObject,pn.inspect=pn.toSource=function(){return""+this};var dn,e=function(t){function n(t,e){if(!(this instanceof n))return new n(t,e);if(this._value=t,this.size=void 0===e?1/0:Math.max(0,e),0===this.size){if(dn)return dn;dn=this}}return t&&(n.__proto__=t),((n.prototype=Object.create(t&&t.prototype)).constructor=n).prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},n.prototype.get=function(t,e){return this.has(t)?this._value:e},n.prototype.includes=function(t){return _t(this._value,t)},n.prototype.slice=function(t,e){var r=this.size;return p(t,e,r)?this:new n(this._value,w(e,r)-y(t,r))},n.prototype.reverse=function(){return this},n.prototype.indexOf=function(t){return _t(this._value,t)?0:-1},n.prototype.lastIndexOf=function(t){return _t(this._value,t)?this.size:-1},n.prototype.__iterate=function(t,e){for(var r=this.size,n=0;n!==r&&!1!==t(this._value,e?r-++n:n++,this););return n},n.prototype.__iterator=function(t,e){var r=this,n=this.size,i=0;return new P(function(){return i===n?N():W(t,e?n-++i:i++,r._value)})},n.prototype.equals=function(t){return t instanceof n?_t(this._value,t._value):Rr(this,t)},n}(Z);function gn(t,e){return function r(n,i,o,t,u,e){if("string"!=typeof o&&!A(o)&&(X(o)||H(o)||ne(o))){if(~n.indexOf(o))throw new TypeError("Cannot convert circular structure to Immutable");n.push(o),u&&""!==t&&u.push(t);var t=i.call(e,t,F(o).map(function(t,e){return r(n,i,t,e,u,o)}),u&&u.slice());return n.pop(),u&&u.pop(),t}return o}([],e||mn,t,"",e&&2<e.length?[]:void 0,{"":t})}function mn(t,e){return z(e)?e.toList():a(e)?e.toMap():e.toSet()}B={version:"4.3.6",Collection:I,Iterable:I,Seq:F,Map:Te,OrderedMap:wr,List:ur,Stack:Er,Set:Kr,OrderedSet:an,PairSorting:Zr,Record:Gr,Range:Hr,Repeat:e,is:_t,fromJS:gn,hash:yt,isImmutable:A,isCollection:f,isKeyed:a,isIndexed:z,isAssociative:b,isOrdered:R,isValueObject:ht,
isPlainObject:ne,isSeq:M,isList:or,isMap:ct,isOrderedMap:ft,isStack:Or,isSet:Ar,isOrderedSet:kr,isRecord:x,get:se,getIn:Jr,has:ue,hasIn:Yr,merge:ze,mergeDeep:Ie,mergeWith:be,mergeDeepWith:Oe,remove:ce,removeIn:le,set:fe,setIn:_e,update:ye,updateIn:he},Xr=I;t.Collection=I,t.Iterable=Xr,t.List=ur,t.Map=Te,t.OrderedMap=wr,t.OrderedSet=an,t.PairSorting=Zr,t.Range=Hr,t.Record=Gr,t.Repeat=e,t.Seq=F,t.Set=Kr,t.Stack=Er,t.default=B,t.fromJS=gn,t.get=se,t.getIn=Jr,t.has=ue,t.hasIn=Yr,t.hash=yt,t.is=_t,t.isAssociative=b,t.isCollection=f,t.isImmutable=A,t.isIndexed=z,t.isKeyed=a,t.isList=or,t.isMap=ct,t.isOrdered=R,t.isOrderedMap=ft,t.isOrderedSet=kr,t.isPlainObject=ne,t.isRecord=x,t.isSeq=M,t.isSet=Ar,t.isStack=Or,t.isValueObject=ht,t.merge=ze,t.mergeDeep=Ie,t.mergeDeepWith=Oe,t.mergeWith=be,t.remove=ce,t.removeIn=le,t.set=fe,t.setIn=_e,t.update=ye,t.updateIn=he,t.version="4.3.6",Object.defineProperty(t,"__esModule",{value:!0})});
pn.setIn=pe,pn.update=de,pn.updateIn=ge,pn.withMutations=Ae,pn.asMutable=ke,pn.asImmutable=Re,pn[B]=pn.entries,pn.toJSON=pn.toObject=Xr.toObject,pn.inspect=pn.toSource=function(){return""+this};var dn,e=function(t){function n(t,e){if(!(this instanceof n))return new n(t,e);if(this._value=t,this.size=void 0===e?1/0:Math.max(0,e),0===this.size){if(dn)return dn;dn=this}}return t&&(n.__proto__=t),((n.prototype=Object.create(t&&t.prototype)).constructor=n).prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},n.prototype.get=function(t,e){return this.has(t)?this._value:e},n.prototype.includes=function(t){return _t(this._value,t)},n.prototype.slice=function(t,e){var r=this.size;return p(t,e,r)?this:new n(this._value,w(e,r)-y(t,r))},n.prototype.reverse=function(){return this},n.prototype.indexOf=function(t){return _t(this._value,t)?0:-1},n.prototype.lastIndexOf=function(t){return _t(this._value,t)?this.size:-1},n.prototype.__iterate=function(t,e){for(var r=this.size,n=0;n!==r&&!1!==t(this._value,e?r-++n:n++,this););return n},n.prototype.__iterator=function(t,e){var r=this,n=this.size,i=0;return new P(function(){return i===n?N():W(t,e?n-++i:i++,r._value)})},n.prototype.equals=function(t){return t instanceof n?_t(this._value,t._value):Rr(this,t)},n}(Z);function gn(t,e){return function r(n,i,o,t,u,e){if("string"!=typeof o&&!A(o)&&(X(o)||H(o)||ne(o))){if(~n.indexOf(o))throw new TypeError("Cannot convert circular structure to Immutable");n.push(o),u&&""!==t&&u.push(t);var t=i.call(e,t,F(o).map(function(t,e){return r(n,i,t,e,u,o)}),u&&u.slice());return n.pop(),u&&u.pop(),t}return o}([],e||mn,t,"",e&&2<e.length?[]:void 0,{"":t})}function mn(t,e){return z(e)?e.toList():a(e)?e.toMap():e.toSet()}B={version:"4.3.7",Collection:I,Iterable:I,Seq:F,Map:Te,OrderedMap:wr,List:ur,Stack:Er,Set:Kr,OrderedSet:an,PairSorting:Zr,Record:Gr,Range:Hr,Repeat:e,is:_t,fromJS:gn,hash:yt,isImmutable:A,isCollection:f,isKeyed:a,isIndexed:z,isAssociative:b,isOrdered:R,isValueObject:ht,
isPlainObject:ne,isSeq:M,isList:or,isMap:ct,isOrderedMap:ft,isStack:Or,isSet:Ar,isOrderedSet:kr,isRecord:x,get:se,getIn:Jr,has:ue,hasIn:Yr,merge:ze,mergeDeep:Ie,mergeWith:be,mergeDeepWith:Oe,remove:ce,removeIn:le,set:fe,setIn:_e,update:ye,updateIn:he},Xr=I;t.Collection=I,t.Iterable=Xr,t.List=ur,t.Map=Te,t.OrderedMap=wr,t.OrderedSet=an,t.PairSorting=Zr,t.Range=Hr,t.Record=Gr,t.Repeat=e,t.Seq=F,t.Set=Kr,t.Stack=Er,t.default=B,t.fromJS=gn,t.get=se,t.getIn=Jr,t.has=ue,t.hasIn=Yr,t.hash=yt,t.is=_t,t.isAssociative=b,t.isCollection=f,t.isImmutable=A,t.isIndexed=z,t.isKeyed=a,t.isList=or,t.isMap=ct,t.isOrdered=R,t.isOrderedMap=ft,t.isOrderedSet=kr,t.isPlainObject=ne,t.isRecord=x,t.isSeq=M,t.isSet=Ar,t.isStack=Or,t.isValueObject=ht,t.merge=ze,t.mergeDeep=Ie,t.mergeDeepWith=Oe,t.mergeWith=be,t.remove=ce,t.removeIn=le,t.set=fe,t.setIn=_e,t.update=ye,t.updateIn=he,t.version="4.3.7",Object.defineProperty(t,"__esModule",{value:!0})});

View File

@ -1,6 +1,6 @@
{
"name": "immutable",
"version": "4.3.6",
"version": "4.3.7",
"description": "Immutable Data Collections",
"license": "MIT",
"homepage": "https://immutable-js.com",

View File

@ -1,17 +0,0 @@
/**
Check if a file path is a binary file.
@example
```
import isBinaryPath = require('is-binary-path');
isBinaryPath('source/unicorn.png');
//=> true
isBinaryPath('source/unicorn.txt');
//=> false
```
*/
declare function isBinaryPath(filePath: string): boolean;
export = isBinaryPath;

View File

@ -1,7 +0,0 @@
'use strict';
const path = require('path');
const binaryExtensions = require('binary-extensions');
const extensions = new Set(binaryExtensions);
module.exports = filePath => extensions.has(path.extname(filePath).slice(1).toLowerCase());

View File

@ -1,40 +0,0 @@
{
"name": "is-binary-path",
"version": "2.1.0",
"description": "Check if a file path is a binary file",
"license": "MIT",
"repository": "sindresorhus/is-binary-path",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"binary",
"extensions",
"extension",
"file",
"path",
"check",
"detect",
"is"
],
"dependencies": {
"binary-extensions": "^2.0.0"
},
"devDependencies": {
"ava": "^1.4.1",
"tsd": "^0.7.2",
"xo": "^0.24.0"
}
}

View File

@ -1,34 +0,0 @@
# is-binary-path [![Build Status](https://travis-ci.org/sindresorhus/is-binary-path.svg?branch=master)](https://travis-ci.org/sindresorhus/is-binary-path)
> Check if a file path is a binary file
## Install
```
$ npm install is-binary-path
```
## Usage
```js
const isBinaryPath = require('is-binary-path');
isBinaryPath('source/unicorn.png');
//=> true
isBinaryPath('source/unicorn.txt');
//=> false
```
## Related
- [binary-extensions](https://github.com/sindresorhus/binary-extensions) - List of binary file extensions
- [is-text-path](https://github.com/sindresorhus/is-text-path) - Check if a filepath is a text file
## License
MIT © [Sindre Sorhus](https://sindresorhus.com), [Paul Miller](https://paulmillr.com)

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014-2018, Jon Schlinkert.
Copyright (c) 2014-present, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,474 @@
'use strict';
const util = require('util');
const braces = require('braces');
const picomatch = require('picomatch');
const utils = require('picomatch/lib/utils');
const isEmptyString = v => v === '' || v === './';
const hasBraces = v => {
const index = v.indexOf('{');
return index > -1 && v.indexOf('}', index) > -1;
};
/**
* Returns an array of strings that match one or more glob patterns.
*
* ```js
* const mm = require('micromatch');
* // mm(list, patterns[, options]);
*
* console.log(mm(['a.js', 'a.txt'], ['*.js']));
* //=> [ 'a.js' ]
* ```
* @param {String|Array<string>} `list` List of strings to match.
* @param {String|Array<string>} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options)
* @return {Array} Returns an array of matches
* @summary false
* @api public
*/
const micromatch = (list, patterns, options) => {
patterns = [].concat(patterns);
list = [].concat(list);
let omit = new Set();
let keep = new Set();
let items = new Set();
let negatives = 0;
let onResult = state => {
items.add(state.output);
if (options && options.onResult) {
options.onResult(state);
}
};
for (let i = 0; i < patterns.length; i++) {
let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true);
let negated = isMatch.state.negated || isMatch.state.negatedExtglob;
if (negated) negatives++;
for (let item of list) {
let matched = isMatch(item, true);
let match = negated ? !matched.isMatch : matched.isMatch;
if (!match) continue;
if (negated) {
omit.add(matched.output);
} else {
omit.delete(matched.output);
keep.add(matched.output);
}
}
}
let result = negatives === patterns.length ? [...items] : [...keep];
let matches = result.filter(item => !omit.has(item));
if (options && matches.length === 0) {
if (options.failglob === true) {
throw new Error(`No matches found for "${patterns.join(', ')}"`);
}
if (options.nonull === true || options.nullglob === true) {
return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns;
}
}
return matches;
};
/**
* Backwards compatibility
*/
micromatch.match = micromatch;
/**
* Returns a matcher function from the given glob `pattern` and `options`.
* The returned function takes a string to match as its only argument and returns
* true if the string is a match.
*
* ```js
* const mm = require('micromatch');
* // mm.matcher(pattern[, options]);
*
* const isMatch = mm.matcher('*.!(*a)');
* console.log(isMatch('a.a')); //=> false
* console.log(isMatch('a.b')); //=> true
* ```
* @param {String} `pattern` Glob pattern
* @param {Object} `options`
* @return {Function} Returns a matcher function.
* @api public
*/
micromatch.matcher = (pattern, options) => picomatch(pattern, options);
/**
* Returns true if **any** of the given glob `patterns` match the specified `string`.
*
* ```js
* const mm = require('micromatch');
* // mm.isMatch(string, patterns[, options]);
*
* console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true
* console.log(mm.isMatch('a.a', 'b.*')); //=> false
* ```
* @param {String} `str` The string to test.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `[options]` See available [options](#options).
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
/**
* Backwards compatibility
*/
micromatch.any = micromatch.isMatch;
/**
* Returns a list of strings that _**do not match any**_ of the given `patterns`.
*
* ```js
* const mm = require('micromatch');
* // mm.not(list, patterns[, options]);
*
* console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a'));
* //=> ['b.b', 'c.c']
* ```
* @param {Array} `list` Array of strings to match.
* @param {String|Array} `patterns` One or more glob pattern to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Array} Returns an array of strings that **do not match** the given patterns.
* @api public
*/
micromatch.not = (list, patterns, options = {}) => {
patterns = [].concat(patterns).map(String);
let result = new Set();
let items = [];
let onResult = state => {
if (options.onResult) options.onResult(state);
items.push(state.output);
};
let matches = new Set(micromatch(list, patterns, { ...options, onResult }));
for (let item of items) {
if (!matches.has(item)) {
result.add(item);
}
}
return [...result];
};
/**
* Returns true if the given `string` contains the given pattern. Similar
* to [.isMatch](#isMatch) but the pattern can match any part of the string.
*
* ```js
* var mm = require('micromatch');
* // mm.contains(string, pattern[, options]);
*
* console.log(mm.contains('aa/bb/cc', '*b'));
* //=> true
* console.log(mm.contains('aa/bb/cc', '*d'));
* //=> false
* ```
* @param {String} `str` The string to match.
* @param {String|Array} `patterns` Glob pattern to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any of the patterns matches any part of `str`.
* @api public
*/
micromatch.contains = (str, pattern, options) => {
if (typeof str !== 'string') {
throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
}
if (Array.isArray(pattern)) {
return pattern.some(p => micromatch.contains(str, p, options));
}
if (typeof pattern === 'string') {
if (isEmptyString(str) || isEmptyString(pattern)) {
return false;
}
if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) {
return true;
}
}
return micromatch.isMatch(str, pattern, { ...options, contains: true });
};
/**
* Filter the keys of the given object with the given `glob` pattern
* and `options`. Does not attempt to match nested keys. If you need this feature,
* use [glob-object][] instead.
*
* ```js
* const mm = require('micromatch');
* // mm.matchKeys(object, patterns[, options]);
*
* const obj = { aa: 'a', ab: 'b', ac: 'c' };
* console.log(mm.matchKeys(obj, '*b'));
* //=> { ab: 'b' }
* ```
* @param {Object} `object` The object with keys to filter.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Object} Returns an object with only keys that match the given patterns.
* @api public
*/
micromatch.matchKeys = (obj, patterns, options) => {
if (!utils.isObject(obj)) {
throw new TypeError('Expected the first argument to be an object');
}
let keys = micromatch(Object.keys(obj), patterns, options);
let res = {};
for (let key of keys) res[key] = obj[key];
return res;
};
/**
* Returns true if some of the strings in the given `list` match any of the given glob `patterns`.
*
* ```js
* const mm = require('micromatch');
* // mm.some(list, patterns[, options]);
*
* console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
* // true
* console.log(mm.some(['foo.js'], ['*.js', '!foo.js']));
* // false
* ```
* @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any `patterns` matches any of the strings in `list`
* @api public
*/
micromatch.some = (list, patterns, options) => {
let items = [].concat(list);
for (let pattern of [].concat(patterns)) {
let isMatch = picomatch(String(pattern), options);
if (items.some(item => isMatch(item))) {
return true;
}
}
return false;
};
/**
* Returns true if every string in the given `list` matches
* any of the given glob `patterns`.
*
* ```js
* const mm = require('micromatch');
* // mm.every(list, patterns[, options]);
*
* console.log(mm.every('foo.js', ['foo.js']));
* // true
* console.log(mm.every(['foo.js', 'bar.js'], ['*.js']));
* // true
* console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
* // false
* console.log(mm.every(['foo.js'], ['*.js', '!foo.js']));
* // false
* ```
* @param {String|Array} `list` The string or array of strings to test.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if all `patterns` matches all of the strings in `list`
* @api public
*/
micromatch.every = (list, patterns, options) => {
let items = [].concat(list);
for (let pattern of [].concat(patterns)) {
let isMatch = picomatch(String(pattern), options);
if (!items.every(item => isMatch(item))) {
return false;
}
}
return true;
};
/**
* Returns true if **all** of the given `patterns` match
* the specified string.
*
* ```js
* const mm = require('micromatch');
* // mm.all(string, patterns[, options]);
*
* console.log(mm.all('foo.js', ['foo.js']));
* // true
*
* console.log(mm.all('foo.js', ['*.js', '!foo.js']));
* // false
*
* console.log(mm.all('foo.js', ['*.js', 'foo.js']));
* // true
*
* console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js']));
* // true
* ```
* @param {String|Array} `str` The string to test.
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
micromatch.all = (str, patterns, options) => {
if (typeof str !== 'string') {
throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
}
return [].concat(patterns).every(p => picomatch(p, options)(str));
};
/**
* Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match.
*
* ```js
* const mm = require('micromatch');
* // mm.capture(pattern, string[, options]);
*
* console.log(mm.capture('test/*.js', 'test/foo.js'));
* //=> ['foo']
* console.log(mm.capture('test/*.js', 'foo/bar.css'));
* //=> null
* ```
* @param {String} `glob` Glob pattern to use for matching.
* @param {String} `input` String to match
* @param {Object} `options` See available [options](#options) for changing how matches are performed
* @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`.
* @api public
*/
micromatch.capture = (glob, input, options) => {
let posix = utils.isWindows(options);
let regex = picomatch.makeRe(String(glob), { ...options, capture: true });
let match = regex.exec(posix ? utils.toPosixSlashes(input) : input);
if (match) {
return match.slice(1).map(v => v === void 0 ? '' : v);
}
};
/**
* Create a regular expression from the given glob `pattern`.
*
* ```js
* const mm = require('micromatch');
* // mm.makeRe(pattern[, options]);
*
* console.log(mm.makeRe('*.js'));
* //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
* ```
* @param {String} `pattern` A glob pattern to convert to regex.
* @param {Object} `options`
* @return {RegExp} Returns a regex created from the given pattern.
* @api public
*/
micromatch.makeRe = (...args) => picomatch.makeRe(...args);
/**
* Scan a glob pattern to separate the pattern into segments. Used
* by the [split](#split) method.
*
* ```js
* const mm = require('micromatch');
* const state = mm.scan(pattern[, options]);
* ```
* @param {String} `pattern`
* @param {Object} `options`
* @return {Object} Returns an object with
* @api public
*/
micromatch.scan = (...args) => picomatch.scan(...args);
/**
* Parse a glob pattern to create the source string for a regular
* expression.
*
* ```js
* const mm = require('micromatch');
* const state = mm.parse(pattern[, options]);
* ```
* @param {String} `glob`
* @param {Object} `options`
* @return {Object} Returns an object with useful properties and output to be used as regex source string.
* @api public
*/
micromatch.parse = (patterns, options) => {
let res = [];
for (let pattern of [].concat(patterns || [])) {
for (let str of braces(String(pattern), options)) {
res.push(picomatch.parse(str, options));
}
}
return res;
};
/**
* Process the given brace `pattern`.
*
* ```js
* const { braces } = require('micromatch');
* console.log(braces('foo/{a,b,c}/bar'));
* //=> [ 'foo/(a|b|c)/bar' ]
*
* console.log(braces('foo/{a,b,c}/bar', { expand: true }));
* //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ]
* ```
* @param {String} `pattern` String with brace pattern to process.
* @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options.
* @return {Array}
* @api public
*/
micromatch.braces = (pattern, options) => {
if (typeof pattern !== 'string') throw new TypeError('Expected a string');
if ((options && options.nobrace === true) || !hasBraces(pattern)) {
return [pattern];
}
return braces(pattern, options);
};
/**
* Expand braces
*/
micromatch.braceExpand = (pattern, options) => {
if (typeof pattern !== 'string') throw new TypeError('Expected a string');
return micromatch.braces(pattern, { ...options, expand: true });
};
/**
* Expose micromatch
*/
// exposed for tests
micromatch.hasBraces = hasBraces;
module.exports = micromatch;

View File

@ -0,0 +1,119 @@
{
"name": "micromatch",
"description": "Glob matching for javascript/node.js. A replacement and faster alternative to minimatch and multimatch.",
"version": "4.0.8",
"homepage": "https://github.com/micromatch/micromatch",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
"(https://github.com/DianeLooney)",
"Amila Welihinda (amilajack.com)",
"Bogdan Chadkin (https://github.com/TrySound)",
"Brian Woodward (https://twitter.com/doowb)",
"Devon Govett (http://badassjs.com)",
"Elan Shanker (https://github.com/es128)",
"Fabrício Matté (https://ultcombo.js.org)",
"Jon Schlinkert (http://twitter.com/jonschlinkert)",
"Martin Kolárik (https://kolarik.sk)",
"Olsten Larck (https://i.am.charlike.online)",
"Paul Miller (paulmillr.com)",
"Tom Byrer (https://github.com/tomByrer)",
"Tyler Akins (http://rumkin.com)",
"Peter Bright <drpizza@quiscalusmexicanus.org> (https://github.com/drpizza)",
"Kuba Juszczyk (https://github.com/ku8ar)"
],
"repository": "micromatch/micromatch",
"bugs": {
"url": "https://github.com/micromatch/micromatch/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=8.6"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"devDependencies": {
"fill-range": "^7.0.1",
"gulp-format-md": "^2.0.0",
"minimatch": "^5.0.1",
"mocha": "^9.2.2",
"time-require": "github:jonschlinkert/time-require"
},
"keywords": [
"bash",
"bracket",
"character-class",
"expand",
"expansion",
"expression",
"extglob",
"extglobs",
"file",
"files",
"filter",
"find",
"glob",
"globbing",
"globs",
"globstar",
"lookahead",
"lookaround",
"lookbehind",
"match",
"matcher",
"matches",
"matching",
"micromatch",
"minimatch",
"multimatch",
"negate",
"negation",
"path",
"pattern",
"patterns",
"posix",
"regex",
"regexp",
"regular",
"shell",
"star",
"wildcard"
],
"verb": {
"toc": "collapsible",
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
},
"related": {
"list": [
"braces",
"expand-brackets",
"extglob",
"fill-range",
"nanomatch"
]
},
"reflinks": [
"extglob",
"fill-range",
"glob-object",
"minimatch",
"multimatch"
]
}
}

View File

@ -1,6 +1,6 @@
MIT License
The MIT License (MIT)
Copyright (c) 2019 Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com), Paul Miller (https://paulmillr.com)
Copyright (c) 2017 [Node.js API collaborators](https://github.com/nodejs/node-addon-api#collaborators)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -0,0 +1,319 @@
NOTE: The default branch has been renamed!
master is now named main
If you have a local clone, you can update it by running:
```shell
git branch -m master main
git fetch origin
git branch -u origin/main main
```
# **node-addon-api module**
This module contains **header-only C++ wrapper classes** which simplify
the use of the C based [Node-API](https://nodejs.org/dist/latest/docs/api/n-api.html)
provided by Node.js when using C++. It provides a C++ object model
and exception handling semantics with low overhead.
There are three options for implementing addons: Node-API, nan, or direct
use of internal V8, libuv, and Node.js libraries. Unless there is a need for
direct access to functionality that is not exposed by Node-API as outlined
in [C/C++ addons](https://nodejs.org/dist/latest/docs/api/addons.html)
in Node.js core, use Node-API. Refer to
[C/C++ addons with Node-API](https://nodejs.org/dist/latest/docs/api/n-api.html)
for more information on Node-API.
Node-API is an ABI stable C interface provided by Node.js for building native
addons. It is independent of the underlying JavaScript runtime (e.g. V8 or ChakraCore)
and is maintained as part of Node.js itself. It is intended to insulate
native addons from changes in the underlying JavaScript engine and allow
modules compiled for one version to run on later versions of Node.js without
recompilation.
The `node-addon-api` module, which is not part of Node.js, preserves the benefits
of the Node-API as it consists only of inline code that depends only on the stable API
provided by Node-API. As such, modules built against one version of Node.js
using node-addon-api should run without having to be rebuilt with newer versions
of Node.js.
It is important to remember that *other* Node.js interfaces such as
`libuv` (included in a project via `#include <uv.h>`) are not ABI-stable across
Node.js major versions. Thus, an addon must use Node-API and/or `node-addon-api`
exclusively and build against a version of Node.js that includes an
implementation of Node-API (meaning an active LTS version of Node.js) in
order to benefit from ABI stability across Node.js major versions. Node.js
provides an [ABI stability guide][] containing a detailed explanation of ABI
stability in general, and the Node-API ABI stability guarantee in particular.
As new APIs are added to Node-API, node-addon-api must be updated to provide
wrappers for those new APIs. For this reason, node-addon-api provides
methods that allow callers to obtain the underlying Node-API handles so
direct calls to Node-API and the use of the objects/methods provided by
node-addon-api can be used together. For example, in order to be able
to use an API for which the node-addon-api does not yet provide a wrapper.
APIs exposed by node-addon-api are generally used to create and
manipulate JavaScript values. Concepts and operations generally map
to ideas specified in the **ECMA262 Language Specification**.
The [Node-API Resource](https://nodejs.github.io/node-addon-examples/) offers an
excellent orientation and tips for developers just getting started with Node-API
and node-addon-api.
- **[Setup](#setup)**
- **[API Documentation](#api)**
- **[Examples](#examples)**
- **[Tests](#tests)**
- **[More resource and info about native Addons](#resources)**
- **[Badges](#badges)**
- **[Code of Conduct](CODE_OF_CONDUCT.md)**
- **[Contributors](#contributors)**
- **[License](#license)**
## **Current version: 7.1.1**
(See [CHANGELOG.md](CHANGELOG.md) for complete Changelog)
[![NPM](https://nodei.co/npm/node-addon-api.png?downloads=true&downloadRank=true)](https://nodei.co/npm/node-addon-api/) [![NPM](https://nodei.co/npm-dl/node-addon-api.png?months=6&height=1)](https://nodei.co/npm/node-addon-api/)
<a name="setup"></a>
node-addon-api is based on [Node-API](https://nodejs.org/api/n-api.html) and supports using different Node-API versions.
This allows addons built with it to run with Node.js versions which support the targeted Node-API version.
**However** the node-addon-api support model is to support only the active LTS Node.js versions. This means that
every year there will be a new major which drops support for the Node.js LTS version which has gone out of service.
The oldest Node.js version supported by the current version of node-addon-api is Node.js 16.x.
## Setup
- [Installation and usage](doc/setup.md)
- [node-gyp](doc/node-gyp.md)
- [cmake-js](doc/cmake-js.md)
- [Conversion tool](doc/conversion-tool.md)
- [Checker tool](doc/checker-tool.md)
- [Generator](doc/generator.md)
- [Prebuild tools](doc/prebuild_tools.md)
<a name="api"></a>
### **API Documentation**
The following is the documentation for node-addon-api.
- [Full Class Hierarchy](doc/hierarchy.md)
- [Addon Structure](doc/addon.md)
- Data Types:
- [Env](doc/env.md)
- [CallbackInfo](doc/callbackinfo.md)
- [Reference](doc/reference.md)
- [Value](doc/value.md)
- [Name](doc/name.md)
- [Symbol](doc/symbol.md)
- [String](doc/string.md)
- [Number](doc/number.md)
- [Date](doc/date.md)
- [BigInt](doc/bigint.md)
- [Boolean](doc/boolean.md)
- [External](doc/external.md)
- [Object](doc/object.md)
- [Array](doc/array.md)
- [ObjectReference](doc/object_reference.md)
- [PropertyDescriptor](doc/property_descriptor.md)
- [Function](doc/function.md)
- [FunctionReference](doc/function_reference.md)
- [ObjectWrap](doc/object_wrap.md)
- [ClassPropertyDescriptor](doc/class_property_descriptor.md)
- [Buffer](doc/buffer.md)
- [ArrayBuffer](doc/array_buffer.md)
- [TypedArray](doc/typed_array.md)
- [TypedArrayOf](doc/typed_array_of.md)
- [DataView](doc/dataview.md)
- [Error Handling](doc/error_handling.md)
- [Error](doc/error.md)
- [TypeError](doc/type_error.md)
- [RangeError](doc/range_error.md)
- [SyntaxError](doc/syntax_error.md)
- [Object Lifetime Management](doc/object_lifetime_management.md)
- [HandleScope](doc/handle_scope.md)
- [EscapableHandleScope](doc/escapable_handle_scope.md)
- [Memory Management](doc/memory_management.md)
- [Async Operations](doc/async_operations.md)
- [AsyncWorker](doc/async_worker.md)
- [AsyncContext](doc/async_context.md)
- [AsyncWorker Variants](doc/async_worker_variants.md)
- [Thread-safe Functions](doc/threadsafe.md)
- [ThreadSafeFunction](doc/threadsafe_function.md)
- [TypedThreadSafeFunction](doc/typed_threadsafe_function.md)
- [Promises](doc/promises.md)
- [Version management](doc/version_management.md)
<a name="examples"></a>
### **Examples**
Are you new to **node-addon-api**? Take a look at our **[examples](https://github.com/nodejs/node-addon-examples)**
- **[Hello World](https://github.com/nodejs/node-addon-examples/tree/main/src/1-getting-started/1_hello_world)**
- **[Pass arguments to a function](https://github.com/nodejs/node-addon-examples/tree/main/src/1-getting-started/2_function_arguments/node-addon-api)**
- **[Callbacks](https://github.com/nodejs/node-addon-examples/tree/main/src/1-getting-started/3_callbacks/node-addon-api)**
- **[Object factory](https://github.com/nodejs/node-addon-examples/tree/main/src/1-getting-started/4_object_factory/node-addon-api)**
- **[Function factory](https://github.com/nodejs/node-addon-examples/tree/main/src/1-getting-started/5_function_factory/node-addon-api)**
- **[Wrapping C++ Object](https://github.com/nodejs/node-addon-examples/tree/main/src/1-getting-started/6_object_wrap/node-addon-api)**
- **[Factory of wrapped object](https://github.com/nodejs/node-addon-examples/tree/main/src/1-getting-started/7_factory_wrap/node-addon-api)**
- **[Passing wrapped object around](https://github.com/nodejs/node-addon-examples/tree/main/src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api)**
<a name="tests"></a>
### **Tests**
To run the **node-addon-api** tests do:
```
npm install
npm test
```
To avoid testing the deprecated portions of the API run
```
npm install
npm test --disable-deprecated
```
To run the tests targeting a specific version of Node-API run
```
npm install
export NAPI_VERSION=X
npm test --NAPI_VERSION=X
```
where X is the version of Node-API you want to target.
To run a specific unit test, filter conditions are available
**Example:**
compile and run only tests on objectwrap.cc and objectwrap.js
```
npm run unit --filter=objectwrap
```
Multiple unit tests cane be selected with wildcards
**Example:**
compile and run all test files ending with "reference" -> function_reference.cc, object_reference.cc, reference.cc
```
npm run unit --filter=*reference
```
Multiple filter conditions can be joined to broaden the test selection
**Example:**
compile and run all tests under folders threadsafe_function and typed_threadsafe_function and also the objectwrap.cc file
npm run unit --filter='*function objectwrap'
### **Debug**
To run the **node-addon-api** tests with `--debug` option:
```
npm run-script dev
```
If you want a faster build, you might use the following option:
```
npm run-script dev:incremental
```
Take a look and get inspired by our **[test suite](https://github.com/nodejs/node-addon-api/tree/HEAD/test)**
### **Benchmarks**
You can run the available benchmarks using the following command:
```
npm run-script benchmark
```
See [benchmark/README.md](benchmark/README.md) for more details about running and adding benchmarks.
<a name="resources"></a>
### **More resource and info about native Addons**
- **[C++ Addons](https://nodejs.org/dist/latest/docs/api/addons.html)**
- **[Node-API](https://nodejs.org/dist/latest/docs/api/n-api.html)**
- **[Node-API - Next Generation Node API for Native Modules](https://youtu.be/-Oniup60Afs)**
- **[How We Migrated Realm JavaScript From NAN to Node-API](https://developer.mongodb.com/article/realm-javascript-nan-to-n-api)**
As node-addon-api's core mission is to expose the plain C Node-API as C++
wrappers, tools that facilitate n-api/node-addon-api providing more
convenient patterns for developing a Node.js add-on with n-api/node-addon-api
can be published to NPM as standalone packages. It is also recommended to tag
such packages with `node-addon-api` to provide more visibility to the community.
Quick links to NPM searches: [keywords:node-addon-api](https://www.npmjs.com/search?q=keywords%3Anode-addon-api).
<a name="other-bindings"></a>
### **Other bindings**
- **[napi-rs](https://napi.rs)** - (`Rust`)
<a name="badges"></a>
### **Badges**
The use of badges is recommended to indicate the minimum version of Node-API
required for the module. This helps to determine which Node.js major versions are
supported. Addon maintainers can consult the [Node-API support matrix][] to determine
which Node.js versions provide a given Node-API version. The following badges are
available:
![Node-API v1 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v1%20Badge.svg)
![Node-API v2 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v2%20Badge.svg)
![Node-API v3 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v3%20Badge.svg)
![Node-API v4 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v4%20Badge.svg)
![Node-API v5 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v5%20Badge.svg)
![Node-API v6 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v6%20Badge.svg)
![Node-API v7 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v7%20Badge.svg)
![Node-API v8 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v8%20Badge.svg)
![Node-API v9 Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20v9%20Badge.svg)
![Node-API Experimental Version Badge](https://github.com/nodejs/abi-stable-node/blob/doc/assets/Node-API%20Experimental%20Version%20Badge.svg)
## **Contributing**
We love contributions from the community to **node-addon-api**!
See [CONTRIBUTING.md](CONTRIBUTING.md) for more details on our philosophy around extending this module.
<a name="contributors"></a>
## Team members
### Active
| Name | GitHub Link |
| ------------------- | ----------------------------------------------------- |
| Anna Henningsen | [addaleax](https://github.com/addaleax) |
| Chengzhong Wu | [legendecas](https://github.com/legendecas) |
| Jack Xia | [JckXia](https://github.com/JckXia) |
| Kevin Eady | [KevinEady](https://github.com/KevinEady) |
| Michael Dawson | [mhdawson](https://github.com/mhdawson) |
| Nicola Del Gobbo | [NickNaso](https://github.com/NickNaso) |
| Vladimir Morozov | [vmoroz](https://github.com/vmoroz) |
### Emeritus
| Name | GitHub Link |
| ------------------- | ----------------------------------------------------- |
| Arunesh Chandra | [aruneshchandra](https://github.com/aruneshchandra) |
| Benjamin Byholm | [kkoopa](https://github.com/kkoopa) |
| Gabriel Schulhof | [gabrielschulhof](https://github.com/gabrielschulhof) |
| Hitesh Kanwathirtha | [digitalinfinity](https://github.com/digitalinfinity) |
| Jason Ginchereau | [jasongin](https://github.com/jasongin) |
| Jim Schlight | [jschlight](https://github.com/jschlight) |
| Sampson Gao | [sampsongao](https://github.com/sampsongao) |
| Taylor Woll | [boingoing](https://github.com/boingoing) |
<a name="license"></a>
Licensed under [MIT](./LICENSE.md)
[ABI stability guide]: https://nodejs.org/en/docs/guides/abi-stability/
[Node-API support matrix]: https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix

View File

@ -0,0 +1,20 @@
{
'variables': {
'NAPI_VERSION%': "<!(node -p \"process.env.NAPI_VERSION || process.versions.napi\")",
'disable_deprecated': "<!(node -p \"process.env['npm_config_disable_deprecated']\")"
},
'conditions': [
['NAPI_VERSION!=""', { 'defines': ['NAPI_VERSION=<@(NAPI_VERSION)'] } ],
['disable_deprecated=="true"', {
'defines': ['NODE_ADDON_API_DISABLE_DEPRECATED']
}],
['OS=="mac"', {
'cflags+': ['-fvisibility=hidden'],
'xcode_settings': {
'OTHER_CFLAGS': ['-fvisibility=hidden']
}
}]
],
'cflags': [ '-Werror', '-Wall', '-Wextra', '-Wpedantic', '-Wunused-parameter' ],
'cflags_cc': [ '-Werror', '-Wall', '-Wextra', '-Wpedantic', '-Wunused-parameter' ]
}

View File

@ -0,0 +1,25 @@
{
'defines': [ 'NAPI_CPP_EXCEPTIONS' ],
'cflags!': [ '-fno-exceptions' ],
'cflags_cc!': [ '-fno-exceptions' ],
'conditions': [
["OS=='win'", {
"defines": [
"_HAS_EXCEPTIONS=1"
],
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1,
'EnablePREfast': 'true',
},
},
}],
["OS=='mac'", {
'xcode_settings': {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'CLANG_CXX_LIBRARY': 'libc++',
'MACOSX_DEPLOYMENT_TARGET': '10.7',
},
}],
],
}

View File

@ -0,0 +1,12 @@
const path = require('path');
const includeDir = path.relative('.', __dirname);
module.exports = {
include: `"${__dirname}"`, // deprecated, can be removed as part of 4.0.0
include_dir: includeDir,
gyp: path.join(includeDir, 'node_api.gyp:nothing'), // deprecated.
targets: path.join(includeDir, 'node_addon_api.gyp'),
isNodeApiBuiltin: true,
needsFlag: false
};

View File

@ -0,0 +1,186 @@
#ifndef SRC_NAPI_INL_DEPRECATED_H_
#define SRC_NAPI_INL_DEPRECATED_H_
////////////////////////////////////////////////////////////////////////////////
// PropertyDescriptor class
////////////////////////////////////////////////////////////////////////////////
template <typename Getter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
const char* utf8name,
Getter getter,
napi_property_attributes attributes,
void* /*data*/) {
using CbData = details::CallbackData<Getter, Napi::Value>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({getter, nullptr});
return PropertyDescriptor({utf8name,
nullptr,
nullptr,
CbData::Wrapper,
nullptr,
nullptr,
attributes,
callbackData});
}
template <typename Getter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
const std::string& utf8name,
Getter getter,
napi_property_attributes attributes,
void* data) {
return Accessor(utf8name.c_str(), getter, attributes, data);
}
template <typename Getter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
napi_value name,
Getter getter,
napi_property_attributes attributes,
void* /*data*/) {
using CbData = details::CallbackData<Getter, Napi::Value>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({getter, nullptr});
return PropertyDescriptor({nullptr,
name,
nullptr,
CbData::Wrapper,
nullptr,
nullptr,
attributes,
callbackData});
}
template <typename Getter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
Name name, Getter getter, napi_property_attributes attributes, void* data) {
napi_value nameValue = name;
return PropertyDescriptor::Accessor(nameValue, getter, attributes, data);
}
template <typename Getter, typename Setter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
const char* utf8name,
Getter getter,
Setter setter,
napi_property_attributes attributes,
void* /*data*/) {
using CbData = details::AccessorCallbackData<Getter, Setter>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({getter, setter, nullptr});
return PropertyDescriptor({utf8name,
nullptr,
nullptr,
CbData::GetterWrapper,
CbData::SetterWrapper,
nullptr,
attributes,
callbackData});
}
template <typename Getter, typename Setter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
const std::string& utf8name,
Getter getter,
Setter setter,
napi_property_attributes attributes,
void* data) {
return Accessor(utf8name.c_str(), getter, setter, attributes, data);
}
template <typename Getter, typename Setter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
napi_value name,
Getter getter,
Setter setter,
napi_property_attributes attributes,
void* /*data*/) {
using CbData = details::AccessorCallbackData<Getter, Setter>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({getter, setter, nullptr});
return PropertyDescriptor({nullptr,
name,
nullptr,
CbData::GetterWrapper,
CbData::SetterWrapper,
nullptr,
attributes,
callbackData});
}
template <typename Getter, typename Setter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
Name name,
Getter getter,
Setter setter,
napi_property_attributes attributes,
void* data) {
napi_value nameValue = name;
return PropertyDescriptor::Accessor(
nameValue, getter, setter, attributes, data);
}
template <typename Callable>
inline PropertyDescriptor PropertyDescriptor::Function(
const char* utf8name,
Callable cb,
napi_property_attributes attributes,
void* /*data*/) {
using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr)));
using CbData = details::CallbackData<Callable, ReturnType>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({cb, nullptr});
return PropertyDescriptor({utf8name,
nullptr,
CbData::Wrapper,
nullptr,
nullptr,
nullptr,
attributes,
callbackData});
}
template <typename Callable>
inline PropertyDescriptor PropertyDescriptor::Function(
const std::string& utf8name,
Callable cb,
napi_property_attributes attributes,
void* data) {
return Function(utf8name.c_str(), cb, attributes, data);
}
template <typename Callable>
inline PropertyDescriptor PropertyDescriptor::Function(
napi_value name,
Callable cb,
napi_property_attributes attributes,
void* /*data*/) {
using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr)));
using CbData = details::CallbackData<Callable, ReturnType>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({cb, nullptr});
return PropertyDescriptor({nullptr,
name,
CbData::Wrapper,
nullptr,
nullptr,
nullptr,
attributes,
callbackData});
}
template <typename Callable>
inline PropertyDescriptor PropertyDescriptor::Function(
Name name, Callable cb, napi_property_attributes attributes, void* data) {
napi_value nameValue = name;
return PropertyDescriptor::Function(nameValue, cb, attributes, data);
}
#endif // !SRC_NAPI_INL_DEPRECATED_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
{
'targets': [
{
'target_name': 'node_addon_api',
'type': 'none',
'sources': [ 'napi.h', 'napi-inl.h' ],
'direct_dependent_settings': {
'include_dirs': [ '.' ],
'includes': ['noexcept.gypi'],
}
},
{
'target_name': 'node_addon_api_except',
'type': 'none',
'sources': [ 'napi.h', 'napi-inl.h' ],
'direct_dependent_settings': {
'include_dirs': [ '.' ],
'includes': ['except.gypi'],
}
},
{
'target_name': 'node_addon_api_maybe',
'type': 'none',
'sources': [ 'napi.h', 'napi-inl.h' ],
'direct_dependent_settings': {
'include_dirs': [ '.' ],
'includes': ['noexcept.gypi'],
'defines': ['NODE_ADDON_API_ENABLE_MAYBE']
}
},
]
}

View File

@ -0,0 +1,9 @@
{
'targets': [
{
'target_name': 'nothing',
'type': 'static_library',
'sources': [ 'nothing.c' ]
}
]
}

View File

@ -0,0 +1,26 @@
{
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
'cflags': [ '-fno-exceptions' ],
'cflags_cc': [ '-fno-exceptions' ],
'conditions': [
["OS=='win'", {
# _HAS_EXCEPTIONS is already defined and set to 0 in common.gypi
#"defines": [
# "_HAS_EXCEPTIONS=0"
#],
"msvs_settings": {
"VCCLCompilerTool": {
'ExceptionHandling': 0,
'EnablePREfast': 'true',
},
},
}],
["OS=='mac'", {
'xcode_settings': {
'CLANG_CXX_LIBRARY': 'libc++',
'MACOSX_DEPLOYMENT_TARGET': '10.7',
'GCC_ENABLE_CPP_EXCEPTIONS': 'NO',
},
}],
],
}

View File

View File

@ -0,0 +1,21 @@
{
"versions": [
{
"version": "*",
"target": {
"node": "active"
},
"response": {
"type": "time-permitting",
"paid": false,
"contact": {
"name": "node-addon-api team",
"url": "https://github.com/nodejs/node-addon-api/issues"
}
},
"backing": [ { "project": "https://github.com/nodejs" },
{ "foundation": "https://openjsf.org/" }
]
}
]
}

View File

@ -0,0 +1,480 @@
{
"bugs": {
"url": "https://github.com/nodejs/node-addon-api/issues"
},
"contributors": [
{
"name": "Abhishek Kumar Singh",
"url": "https://github.com/abhi11210646"
},
{
"name": "Alba Mendez",
"url": "https://github.com/jmendeth"
},
{
"name": "Alexander Floh",
"url": "https://github.com/alexanderfloh"
},
{
"name": "Ammar Faizi",
"url": "https://github.com/ammarfaizi2"
},
{
"name": "András Timár, Dr",
"url": "https://github.com/timarandras"
},
{
"name": "Andrew Petersen",
"url": "https://github.com/kirbysayshi"
},
{
"name": "Anisha Rohra",
"url": "https://github.com/anisha-rohra"
},
{
"name": "Anna Henningsen",
"url": "https://github.com/addaleax"
},
{
"name": "Arnaud Botella",
"url": "https://github.com/BotellaA"
},
{
"name": "Arunesh Chandra",
"url": "https://github.com/aruneshchandra"
},
{
"name": "Azlan Mukhtar",
"url": "https://github.com/azlan"
},
{
"name": "Ben Berman",
"url": "https://github.com/rivertam"
},
{
"name": "Benjamin Byholm",
"url": "https://github.com/kkoopa"
},
{
"name": "Bill Gallafent",
"url": "https://github.com/gallafent"
},
{
"name": "blagoev",
"url": "https://github.com/blagoev"
},
{
"name": "Bruce A. MacNaughton",
"url": "https://github.com/bmacnaughton"
},
{
"name": "Cory Mickelson",
"url": "https://github.com/corymickelson"
},
{
"name": "Daniel Bevenius",
"url": "https://github.com/danbev"
},
{
"name": "Dante Calderón",
"url": "https://github.com/dantehemerson"
},
{
"name": "Darshan Sen",
"url": "https://github.com/RaisinTen"
},
{
"name": "David Halls",
"url": "https://github.com/davedoesdev"
},
{
"name": "Deepak Rajamohan",
"url": "https://github.com/deepakrkris"
},
{
"name": "Dmitry Ashkadov",
"url": "https://github.com/dmitryash"
},
{
"name": "Dongjin Na",
"url": "https://github.com/nadongguri"
},
{
"name": "Doni Rubiagatra",
"url": "https://github.com/rubiagatra"
},
{
"name": "Eric Bickle",
"url": "https://github.com/ebickle"
},
{
"name": "extremeheat",
"url": "https://github.com/extremeheat"
},
{
"name": "Feng Yu",
"url": "https://github.com/F3n67u"
},
{
"name": "Ferdinand Holzer",
"url": "https://github.com/fholzer"
},
{
"name": "Gabriel Schulhof",
"url": "https://github.com/gabrielschulhof"
},
{
"name": "Guenter Sandner",
"url": "https://github.com/gms1"
},
{
"name": "Gus Caplan",
"url": "https://github.com/devsnek"
},
{
"name": "Helio Frota",
"url": "https://github.com/helio-frota"
},
{
"name": "Hitesh Kanwathirtha",
"url": "https://github.com/digitalinfinity"
},
{
"name": "ikokostya",
"url": "https://github.com/ikokostya"
},
{
"name": "Jack Xia",
"url": "https://github.com/JckXia"
},
{
"name": "Jake Barnes",
"url": "https://github.com/DuBistKomisch"
},
{
"name": "Jake Yoon",
"url": "https://github.com/yjaeseok"
},
{
"name": "Jason Ginchereau",
"url": "https://github.com/jasongin"
},
{
"name": "Jenny",
"url": "https://github.com/egg-bread"
},
{
"name": "Jeroen Janssen",
"url": "https://github.com/japj"
},
{
"name": "Jim Schlight",
"url": "https://github.com/jschlight"
},
{
"name": "Jinho Bang",
"url": "https://github.com/romandev"
},
{
"name": "José Expósito",
"url": "https://github.com/JoseExposito"
},
{
"name": "joshgarde",
"url": "https://github.com/joshgarde"
},
{
"name": "Julian Mesa",
"url": "https://github.com/julianmesa-gitkraken"
},
{
"name": "Kasumi Hanazuki",
"url": "https://github.com/hanazuki"
},
{
"name": "Kelvin",
"url": "https://github.com/kelvinhammond"
},
{
"name": "Kevin Eady",
"url": "https://github.com/KevinEady"
},
{
"name": "Kévin VOYER",
"url": "https://github.com/kecsou"
},
{
"name": "kidneysolo",
"url": "https://github.com/kidneysolo"
},
{
"name": "Koki Nishihara",
"url": "https://github.com/Nishikoh"
},
{
"name": "Konstantin Tarkus",
"url": "https://github.com/koistya"
},
{
"name": "Kyle Farnung",
"url": "https://github.com/kfarnung"
},
{
"name": "Kyle Kovacs",
"url": "https://github.com/nullromo"
},
{
"name": "legendecas",
"url": "https://github.com/legendecas"
},
{
"name": "LongYinan",
"url": "https://github.com/Brooooooklyn"
},
{
"name": "Lovell Fuller",
"url": "https://github.com/lovell"
},
{
"name": "Luciano Martorella",
"url": "https://github.com/lmartorella"
},
{
"name": "mastergberry",
"url": "https://github.com/mastergberry"
},
{
"name": "Mathias Küsel",
"url": "https://github.com/mathiask88"
},
{
"name": "Mathias Stearn",
"url": "https://github.com/RedBeard0531"
},
{
"name": "Matteo Collina",
"url": "https://github.com/mcollina"
},
{
"name": "Michael Dawson",
"url": "https://github.com/mhdawson"
},
{
"name": "Michael Price",
"url": "https://github.com/mikepricedev"
},
{
"name": "Michele Campus",
"url": "https://github.com/kYroL01"
},
{
"name": "Mikhail Cheshkov",
"url": "https://github.com/mcheshkov"
},
{
"name": "nempoBu4",
"url": "https://github.com/nempoBu4"
},
{
"name": "Nicola Del Gobbo",
"url": "https://github.com/NickNaso"
},
{
"name": "Nick Soggin",
"url": "https://github.com/iSkore"
},
{
"name": "Nikolai Vavilov",
"url": "https://github.com/seishun"
},
{
"name": "Nurbol Alpysbayev",
"url": "https://github.com/anurbol"
},
{
"name": "pacop",
"url": "https://github.com/pacop"
},
{
"name": "Peter Šándor",
"url": "https://github.com/petersandor"
},
{
"name": "Philipp Renoth",
"url": "https://github.com/DaAitch"
},
{
"name": "rgerd",
"url": "https://github.com/rgerd"
},
{
"name": "Richard Lau",
"url": "https://github.com/richardlau"
},
{
"name": "Rolf Timmermans",
"url": "https://github.com/rolftimmermans"
},
{
"name": "Ross Weir",
"url": "https://github.com/ross-weir"
},
{
"name": "Ryuichi Okumura",
"url": "https://github.com/okuryu"
},
{
"name": "Saint Gabriel",
"url": "https://github.com/chineduG"
},
{
"name": "Sampson Gao",
"url": "https://github.com/sampsongao"
},
{
"name": "Sam Roberts",
"url": "https://github.com/sam-github"
},
{
"name": "strager",
"url": "https://github.com/strager"
},
{
"name": "Taylor Woll",
"url": "https://github.com/boingoing"
},
{
"name": "Thomas Gentilhomme",
"url": "https://github.com/fraxken"
},
{
"name": "Tim Rach",
"url": "https://github.com/timrach"
},
{
"name": "Tobias Nießen",
"url": "https://github.com/tniessen"
},
{
"name": "todoroff",
"url": "https://github.com/todoroff"
},
{
"name": "Toyo Li",
"url": "https://github.com/toyobayashi"
},
{
"name": "Tux3",
"url": "https://github.com/tux3"
},
{
"name": "Vlad Velmisov",
"url": "https://github.com/Velmisov"
},
{
"name": "Vladimir Morozov",
"url": "https://github.com/vmoroz"
},
{
"name": "WenheLI",
"url": "https://github.com/WenheLI"
},
{
"name": "Xuguang Mei",
"url": "https://github.com/meixg"
},
{
"name": "Yohei Kishimoto",
"url": "https://github.com/morokosi"
},
{
"name": "Yulong Wang",
"url": "https://github.com/fs-eire"
},
{
"name": "Ziqiu Zhao",
"url": "https://github.com/ZzqiZQute"
},
{
"name": "Feng Yu",
"url": "https://github.com/F3n67u"
},
{
"name": "wanlu wang",
"url": "https://github.com/wanlu"
},
{
"name": "Caleb Hearon",
"url": "https://github.com/chearon"
},
{
"name": "Marx",
"url": "https://github.com/MarxJiao"
},
{
"name": "Ömer AKGÜL",
"url": "https://github.com/tuhalf"
}
],
"description": "Node.js API (Node-API)",
"devDependencies": {
"benchmark": "^2.1.4",
"bindings": "^1.5.0",
"clang-format": "^1.4.0",
"eslint": "^7.32.0",
"eslint-config-semistandard": "^16.0.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"fs-extra": "^11.1.1",
"path": "^0.12.7",
"pre-commit": "^1.2.2",
"safe-buffer": "^5.1.1"
},
"directories": {},
"gypfile": false,
"homepage": "https://github.com/nodejs/node-addon-api",
"keywords": [
"n-api",
"napi",
"addon",
"native",
"bindings",
"c",
"c++",
"nan",
"node-addon-api"
],
"license": "MIT",
"main": "index.js",
"name": "node-addon-api",
"readme": "README.md",
"repository": {
"type": "git",
"url": "git://github.com/nodejs/node-addon-api.git"
},
"files": [
"*.{c,h,gyp,gypi}",
"package-support.json",
"tools/"
],
"scripts": {
"prebenchmark": "node-gyp rebuild -C benchmark",
"benchmark": "node benchmark",
"pretest": "node-gyp rebuild -C test",
"test": "node test",
"test:debug": "node-gyp rebuild -C test --debug && NODE_API_BUILD_CONFIG=Debug node ./test/index.js",
"predev": "node-gyp rebuild -C test --debug",
"dev": "node test",
"predev:incremental": "node-gyp configure build -C test --debug",
"dev:incremental": "node test",
"doc": "doxygen doc/Doxyfile",
"lint": "node tools/eslint-format && node tools/clang-format",
"lint:fix": "node tools/clang-format --fix && node tools/eslint-format --fix"
},
"pre-commit": "lint",
"version": "7.1.1",
"support": true
}

View File

@ -0,0 +1,73 @@
# Tools
## clang-format
The clang-format checking tools is designed to check changed lines of code compared to given git-refs.
## Migration Script
The migration tool is designed to reduce repetitive work in the migration process. However, the script is not aiming to convert every thing for you. There are usually some small fixes and major reconstruction required.
### How To Use
To run the conversion script, first make sure you have the latest `node-addon-api` in your `node_modules` directory.
```
npm install node-addon-api
```
Then run the script passing your project directory
```
node ./node_modules/node-addon-api/tools/conversion.js ./
```
After finish, recompile and debug things that are missed by the script.
### Quick Fixes
Here is the list of things that can be fixed easily.
1. Change your methods' return value to void if it doesn't return value to JavaScript.
2. Use `.` to access attribute or to invoke member function in Napi::Object instead of `->`.
3. `Napi::New(env, value);` to `Napi::[Type]::New(env, value);
### Major Reconstructions
The implementation of `Napi::ObjectWrap` is significantly different from NAN's. `Napi::ObjectWrap` takes a pointer to the wrapped object and creates a reference to the wrapped object inside ObjectWrap constructor. `Napi::ObjectWrap` also associates wrapped object's instance methods to Javascript module instead of static methods like NAN.
So if you use Nan::ObjectWrap in your module, you will need to execute the following steps.
1. Convert your [ClassName]::New function to a constructor function that takes a `Napi::CallbackInfo`. Declare it as
```
[ClassName](const Napi::CallbackInfo& info);
```
and define it as
```
[ClassName]::[ClassName](const Napi::CallbackInfo& info) : Napi::ObjectWrap<[ClassName]>(info){
...
}
```
This way, the `Napi::ObjectWrap` constructor will be invoked after the object has been instantiated and `Napi::ObjectWrap` can use the `this` pointer to create a reference to the wrapped object.
2. Move your original constructor code into the new constructor. Delete your original constructor.
3. In your class initialization function, associate native methods in the following way.
```
Napi::FunctionReference constructor;
void [ClassName]::Init(Napi::Env env, Napi::Object exports, Napi::Object module) {
Napi::HandleScope scope(env);
Napi::Function ctor = DefineClass(env, "Canvas", {
InstanceMethod<&[ClassName]::Func1>("Func1"),
InstanceMethod<&[ClassName]::Func2>("Func2"),
InstanceAccessor<&[ClassName]::ValueGetter>("Value"),
StaticMethod<&[ClassName]::StaticMethod>("MethodName"),
InstanceValue("Value", Napi::[Type]::New(env, value)),
});
constructor = Napi::Persistent(ctor);
constructor .SuppressDestruct();
exports.Set("[ClassName]", ctor);
}
```
4. In function where you need to Unwrap the ObjectWrap in NAN like `[ClassName]* native = Nan::ObjectWrap::Unwrap<[ClassName]>(info.This());`, use `this` pointer directly as the unwrapped object as each ObjectWrap instance is associated with a unique object instance.
If you still find issues after following this guide, please leave us an issue describing your problem and we will try to resolve it.

View File

@ -0,0 +1,99 @@
'use strict';
// Descend into a directory structure and, for each file matching *.node, output
// based on the imports found in the file whether it's an N-API module or not.
const fs = require('fs');
const path = require('path');
// Read the output of the command, break it into lines, and use the reducer to
// decide whether the file is an N-API module or not.
function checkFile (file, command, argv, reducer) {
const child = require('child_process').spawn(command, argv, {
stdio: ['inherit', 'pipe', 'inherit']
});
let leftover = '';
let isNapi;
child.stdout.on('data', (chunk) => {
if (isNapi === undefined) {
chunk = (leftover + chunk.toString()).split(/[\r\n]+/);
leftover = chunk.pop();
isNapi = chunk.reduce(reducer, isNapi);
if (isNapi !== undefined) {
child.kill();
}
}
});
child.on('close', (code, signal) => {
if ((code === null && signal !== null) || (code !== 0)) {
console.log(
command + ' exited with code: ' + code + ' and signal: ' + signal);
} else {
// Green if it's a N-API module, red otherwise.
console.log(
'\x1b[' + (isNapi ? '42' : '41') + 'm' +
(isNapi ? ' N-API' : 'Not N-API') +
'\x1b[0m: ' + file);
}
});
}
// Use nm -a to list symbols.
function checkFileUNIX (file) {
checkFile(file, 'nm', ['-a', file], (soFar, line) => {
if (soFar === undefined) {
line = line.match(/([0-9a-f]*)? ([a-zA-Z]) (.*$)/);
if (line[2] === 'U') {
if (/^napi/.test(line[3])) {
soFar = true;
}
}
}
return soFar;
});
}
// Use dumpbin /imports to list symbols.
function checkFileWin32 (file) {
checkFile(file, 'dumpbin', ['/imports', file], (soFar, line) => {
if (soFar === undefined) {
line = line.match(/([0-9a-f]*)? +([a-zA-Z0-9]) (.*$)/);
if (line && /^napi/.test(line[line.length - 1])) {
soFar = true;
}
}
return soFar;
});
}
// Descend into a directory structure and pass each file ending in '.node' to
// one of the above checks, depending on the OS.
function recurse (top) {
fs.readdir(top, (error, items) => {
if (error) {
throw new Error('error reading directory ' + top + ': ' + error);
}
items.forEach((item) => {
item = path.join(top, item);
fs.stat(item, ((item) => (error, stats) => {
if (error) {
throw new Error('error about ' + item + ': ' + error);
}
if (stats.isDirectory()) {
recurse(item);
} else if (/[.]node$/.test(item) &&
// Explicitly ignore files called 'nothing.node' because they are
// artefacts of node-addon-api having identified a version of
// Node.js that ships with a correct implementation of N-API.
path.basename(item) !== 'nothing.node') {
process.platform === 'win32'
? checkFileWin32(item)
: checkFileUNIX(item);
}
})(item));
});
});
}
// Start with the directory given on the command line or the current directory
// if nothing was given.
recurse(process.argv.length > 3 ? process.argv[2] : '.');

View File

@ -0,0 +1,71 @@
#!/usr/bin/env node
const spawn = require('child_process').spawnSync;
const path = require('path');
const filesToCheck = ['*.h', '*.cc'];
const FORMAT_START = process.env.FORMAT_START || 'main';
function main (args) {
let fix = false;
while (args.length > 0) {
switch (args[0]) {
case '-f':
case '--fix':
fix = true;
break;
default:
}
args.shift();
}
const clangFormatPath = path.dirname(require.resolve('clang-format'));
const binary = process.platform === 'win32'
? 'node_modules\\.bin\\clang-format.cmd'
: 'node_modules/.bin/clang-format';
const options = ['--binary=' + binary, '--style=file'];
if (fix) {
options.push(FORMAT_START);
} else {
options.push('--diff', FORMAT_START);
}
const gitClangFormatPath = path.join(clangFormatPath, 'bin/git-clang-format');
const result = spawn(
'python',
[gitClangFormatPath, ...options, '--', ...filesToCheck],
{ encoding: 'utf-8' }
);
if (result.stderr) {
console.error('Error running git-clang-format:', result.stderr);
return 2;
}
const clangFormatOutput = result.stdout.trim();
// Bail fast if in fix mode.
if (fix) {
console.log(clangFormatOutput);
return 0;
}
// Detect if there is any complains from clang-format
if (
clangFormatOutput !== '' &&
clangFormatOutput !== 'no modified files to format' &&
clangFormatOutput !== 'clang-format did not modify any files'
) {
console.error(clangFormatOutput);
const fixCmd = 'npm run lint:fix';
console.error(`
ERROR: please run "${fixCmd}" to format changes in your commit
Note that when running the command locally, please keep your local
main branch and working branch up to date with nodejs/node-addon-api
to exclude un-related complains.
Or you can run "env FORMAT_START=upstream/main ${fixCmd}".`);
return 1;
}
}
if (require.main === module) {
process.exitCode = main(process.argv.slice(2));
}

View File

@ -0,0 +1,301 @@
#! /usr/bin/env node
'use strict';
const fs = require('fs');
const path = require('path');
const args = process.argv.slice(2);
const dir = args[0];
if (!dir) {
console.log('Usage: node ' + path.basename(__filename) + ' <target-dir>');
process.exit(1);
}
const NodeApiVersion = require('../package.json').version;
const disable = args[1];
let ConfigFileOperations;
if (disable !== '--disable' && dir !== '--disable') {
ConfigFileOperations = {
'package.json': [
[/([ ]*)"dependencies": {/g, '$1"dependencies": {\n$1 "node-addon-api": "' + NodeApiVersion + '",'],
[/[ ]*"nan": *"[^"]+"(,|)[\n\r]/g, '']
],
'binding.gyp': [
[/([ ]*)'include_dirs': \[/g, '$1\'include_dirs\': [\n$1 \'<!(node -p "require(\\\'node-addon-api\\\').include_dir")\','],
[/([ ]*)"include_dirs": \[/g, '$1"include_dirs": [\n$1 "<!(node -p \\"require(\'node-addon-api\').include_dir\\")",'],
[/[ ]*("|')<!\(node -e ("|'|\\"|\\')require\(("|'|\\"|\\')nan("|'|\\"|\\')\)("|'|\\"|\\')\)("|')(,|)[\r\n]/g, ''],
[/([ ]*)("|')target_name("|'): ("|')(.+?)("|'),/g, '$1$2target_name$2: $4$5$6,\n $2cflags!$2: [ $2-fno-exceptions$2 ],\n $2cflags_cc!$2: [ $2-fno-exceptions$2 ],\n $2xcode_settings$2: { $2GCC_ENABLE_CPP_EXCEPTIONS$2: $2YES$2,\n $2CLANG_CXX_LIBRARY$2: $2libc++$2,\n $2MACOSX_DEPLOYMENT_TARGET$2: $210.7$2,\n },\n $2msvs_settings$2: {\n $2VCCLCompilerTool$2: { $2ExceptionHandling$2: 1 },\n },']
]
};
} else {
ConfigFileOperations = {
'package.json': [
[/([ ]*)"dependencies": {/g, '$1"dependencies": {\n$1 "node-addon-api": "' + NodeApiVersion + '",'],
[/[ ]*"nan": *"[^"]+"(,|)[\n\r]/g, '']
],
'binding.gyp': [
[/([ ]*)'include_dirs': \[/g, '$1\'include_dirs\': [\n$1 \'<!(node -p "require(\\\'node-addon-api\\\').include_dir")\','],
[/([ ]*)"include_dirs": \[/g, '$1"include_dirs": [\n$1 "<!(node -p \'require(\\"node-addon-api\\").include_dir\')",'],
[/[ ]*("|')<!\(node -e ("|'|\\"|\\')require\(("|'|\\"|\\')nan("|'|\\"|\\')\)("|'|\\"|\\')\)("|')(,|)[\r\n]/g, ''],
[/([ ]*)("|')target_name("|'): ("|')(.+?)("|'),/g, '$1$2target_name$2: $4$5$6,\n $2cflags!$2: [ $2-fno-exceptions$2 ],\n $2cflags_cc!$2: [ $2-fno-exceptions$2 ],\n $2defines$2: [ $2NAPI_DISABLE_CPP_EXCEPTIONS$2 ],\n $2conditions$2: [\n [\'OS=="win"\', { $2defines$2: [ $2_HAS_EXCEPTIONS=1$2 ] }]\n ]']
]
};
}
const SourceFileOperations = [
[/Nan::SetMethod\(target,[\s]*"(.*)"[\s]*,[\s]*([^)]+)\)/g, 'exports.Set(Napi::String::New(env, "$1"), Napi::Function::New(env, $2))'],
[/v8::Local<v8::FunctionTemplate>\s+(\w+)\s*=\s*Nan::New<FunctionTemplate>\([\w\d:]+\);(?:\w+->Reset\(\1\))?\s+\1->SetClassName\(Nan::String::New\("(\w+)"\)\);/g, 'Napi::Function $1 = DefineClass(env, "$2", {'],
[/Local<FunctionTemplate>\s+(\w+)\s*=\s*Nan::New<FunctionTemplate>\([\w\d:]+\);\s+(\w+)\.Reset\((\1)\);\s+\1->SetClassName\((Nan::String::New|Nan::New<(v8::)*String>)\("(.+?)"\)\);/g, 'Napi::Function $1 = DefineClass(env, "$6", {'],
[/Local<FunctionTemplate>\s+(\w+)\s*=\s*Nan::New<FunctionTemplate>\([\w\d:]+\);(?:\w+->Reset\(\1\))?\s+\1->SetClassName\(Nan::String::New\("(\w+)"\)\);/g, 'Napi::Function $1 = DefineClass(env, "$2", {'],
[/Nan::New<v8::FunctionTemplate>\(([\w\d:]+)\)->GetFunction\(\)/g, 'Napi::Function::New(env, $1)'],
[/Nan::New<FunctionTemplate>\(([\w\d:]+)\)->GetFunction()/g, 'Napi::Function::New(env, $1);'],
[/Nan::New<v8::FunctionTemplate>\(([\w\d:]+)\)/g, 'Napi::Function::New(env, $1)'],
[/Nan::New<FunctionTemplate>\(([\w\d:]+)\)/g, 'Napi::Function::New(env, $1)'],
// FunctionTemplate to FunctionReference
[/Nan::Persistent<(v8::)*FunctionTemplate>/g, 'Napi::FunctionReference'],
[/Nan::Persistent<(v8::)*Function>/g, 'Napi::FunctionReference'],
[/v8::Local<v8::FunctionTemplate>/g, 'Napi::FunctionReference'],
[/Local<FunctionTemplate>/g, 'Napi::FunctionReference'],
[/v8::FunctionTemplate/g, 'Napi::FunctionReference'],
[/FunctionTemplate/g, 'Napi::FunctionReference'],
[/([ ]*)Nan::SetPrototypeMethod\(\w+, "(\w+)", (\w+)\);/g, '$1InstanceMethod("$2", &$3),'],
[/([ ]*)(?:\w+\.Reset\(\w+\);\s+)?\(target\)\.Set\("(\w+)",\s*Nan::GetFunction\((\w+)\)\);/gm,
'});\n\n' +
'$1constructor = Napi::Persistent($3);\n' +
'$1constructor.SuppressDestruct();\n' +
'$1target.Set("$2", $3);'],
// TODO: Other attribute combinations
[/static_cast<PropertyAttribute>\(ReadOnly\s*\|\s*DontDelete\)/gm,
'static_cast<napi_property_attributes>(napi_enumerable | napi_configurable)'],
[/([\w\d:<>]+?)::Cast\((.+?)\)/g, '$2.As<$1>()'],
[/\*Nan::Utf8String\(([^)]+)\)/g, '$1->As<Napi::String>().Utf8Value().c_str()'],
[/Nan::Utf8String +(\w+)\(([^)]+)\)/g, 'std::string $1 = $2.As<Napi::String>()'],
[/Nan::Utf8String/g, 'std::string'],
[/v8::String::Utf8Value (.+?)\((.+?)\)/g, 'Napi::String $1(env, $2)'],
[/String::Utf8Value (.+?)\((.+?)\)/g, 'Napi::String $1(env, $2)'],
[/\.length\(\)/g, '.Length()'],
[/Nan::MakeCallback\(([^,]+),[\s\\]+([^,]+),/gm, '$2.MakeCallback($1,'],
[/class\s+(\w+)\s*:\s*public\s+Nan::ObjectWrap/g, 'class $1 : public Napi::ObjectWrap<$1>'],
[/(\w+)\(([^)]*)\)\s*:\s*Nan::ObjectWrap\(\)\s*(,)?/gm, '$1($2) : Napi::ObjectWrap<$1>()$3'],
// HandleOKCallback to OnOK
[/HandleOKCallback/g, 'OnOK'],
// HandleErrorCallback to OnError
[/HandleErrorCallback/g, 'OnError'],
// ex. .As<Function>() to .As<Napi::Object>()
[/\.As<v8::(Value|Boolean|String|Number|Object|Array|Symbol|External|Function)>\(\)/g, '.As<Napi::$1>()'],
[/\.As<(Value|Boolean|String|Number|Object|Array|Symbol|External|Function)>\(\)/g, '.As<Napi::$1>()'],
// ex. Nan::New<Number>(info[0]) to Napi::Number::New(info[0])
[/Nan::New<(v8::)*Integer>\((.+?)\)/g, 'Napi::Number::New(env, $2)'],
[/Nan::New\(([0-9.]+)\)/g, 'Napi::Number::New(env, $1)'],
[/Nan::New<(v8::)*String>\("(.+?)"\)/g, 'Napi::String::New(env, "$2")'],
[/Nan::New\("(.+?)"\)/g, 'Napi::String::New(env, "$1")'],
[/Nan::New<(v8::)*(.+?)>\(\)/g, 'Napi::$2::New(env)'],
[/Nan::New<(.+?)>\(\)/g, 'Napi::$1::New(env)'],
[/Nan::New<(v8::)*(.+?)>\(/g, 'Napi::$2::New(env, '],
[/Nan::New<(.+?)>\(/g, 'Napi::$1::New(env, '],
[/Nan::NewBuffer\(/g, 'Napi::Buffer<char>::New(env, '],
// TODO: Properly handle this
[/Nan::New\(/g, 'Napi::New(env, '],
[/\.IsInt32\(\)/g, '.IsNumber()'],
[/->IsInt32\(\)/g, '.IsNumber()'],
[/(.+?)->BooleanValue\(\)/g, '$1.As<Napi::Boolean>().Value()'],
[/(.+?)->Int32Value\(\)/g, '$1.As<Napi::Number>().Int32Value()'],
[/(.+?)->Uint32Value\(\)/g, '$1.As<Napi::Number>().Uint32Value()'],
[/(.+?)->IntegerValue\(\)/g, '$1.As<Napi::Number>().Int64Value()'],
[/(.+?)->NumberValue\(\)/g, '$1.As<Napi::Number>().DoubleValue()'],
// ex. Nan::To<bool>(info[0]) to info[0].Value()
[/Nan::To<v8::(Boolean|String|Number|Object|Array|Symbol|Function)>\((.+?)\)/g, '$2.To<Napi::$1>()'],
[/Nan::To<(Boolean|String|Number|Object|Array|Symbol|Function)>\((.+?)\)/g, '$2.To<Napi::$1>()'],
// ex. Nan::To<bool>(info[0]) to info[0].As<Napi::Boolean>().Value()
[/Nan::To<bool>\((.+?)\)/g, '$1.As<Napi::Boolean>().Value()'],
// ex. Nan::To<int>(info[0]) to info[0].As<Napi::Number>().Int32Value()
[/Nan::To<int>\((.+?)\)/g, '$1.As<Napi::Number>().Int32Value()'],
// ex. Nan::To<int32_t>(info[0]) to info[0].As<Napi::Number>().Int32Value()
[/Nan::To<int32_t>\((.+?)\)/g, '$1.As<Napi::Number>().Int32Value()'],
// ex. Nan::To<uint32_t>(info[0]) to info[0].As<Napi::Number>().Uint32Value()
[/Nan::To<uint32_t>\((.+?)\)/g, '$1.As<Napi::Number>().Uint32Value()'],
// ex. Nan::To<int64_t>(info[0]) to info[0].As<Napi::Number>().Int64Value()
[/Nan::To<int64_t>\((.+?)\)/g, '$1.As<Napi::Number>().Int64Value()'],
// ex. Nan::To<float>(info[0]) to info[0].As<Napi::Number>().FloatValue()
[/Nan::To<float>\((.+?)\)/g, '$1.As<Napi::Number>().FloatValue()'],
// ex. Nan::To<double>(info[0]) to info[0].As<Napi::Number>().DoubleValue()
[/Nan::To<double>\((.+?)\)/g, '$1.As<Napi::Number>().DoubleValue()'],
[/Nan::New\((\w+)\)->HasInstance\((\w+)\)/g, '$2.InstanceOf($1.Value())'],
[/Nan::Has\(([^,]+),\s*/gm, '($1).Has('],
[/\.Has\([\s|\\]*Nan::New<(v8::)*String>\(([^)]+)\)\)/gm, '.Has($1)'],
[/\.Has\([\s|\\]*Nan::New\(([^)]+)\)\)/gm, '.Has($1)'],
[/Nan::Get\(([^,]+),\s*/gm, '($1).Get('],
[/\.Get\([\s|\\]*Nan::New<(v8::)*String>\(([^)]+)\)\)/gm, '.Get($1)'],
[/\.Get\([\s|\\]*Nan::New\(([^)]+)\)\)/gm, '.Get($1)'],
[/Nan::Set\(([^,]+),\s*/gm, '($1).Set('],
[/\.Set\([\s|\\]*Nan::New<(v8::)*String>\(([^)]+)\)\s*,/gm, '.Set($1,'],
[/\.Set\([\s|\\]*Nan::New\(([^)]+)\)\s*,/gm, '.Set($1,'],
// ex. node::Buffer::HasInstance(info[0]) to info[0].IsBuffer()
[/node::Buffer::HasInstance\((.+?)\)/g, '$1.IsBuffer()'],
// ex. node::Buffer::Length(info[0]) to info[0].Length()
[/node::Buffer::Length\((.+?)\)/g, '$1.As<Napi::Buffer<char>>().Length()'],
// ex. node::Buffer::Data(info[0]) to info[0].Data()
[/node::Buffer::Data\((.+?)\)/g, '$1.As<Napi::Buffer<char>>().Data()'],
[/Nan::CopyBuffer\(/g, 'Napi::Buffer::Copy(env, '],
// Nan::AsyncQueueWorker(worker)
[/Nan::AsyncQueueWorker\((.+)\);/g, '$1.Queue();'],
[/Nan::(Undefined|Null|True|False)\(\)/g, 'env.$1()'],
// Nan::ThrowError(error) to Napi::Error::New(env, error).ThrowAsJavaScriptException()
[/([ ]*)return Nan::Throw(\w*?)Error\((.+?)\);/g, '$1Napi::$2Error::New(env, $3).ThrowAsJavaScriptException();\n$1return env.Null();'],
[/Nan::Throw(\w*?)Error\((.+?)\);\n(\s*)return;/g, 'Napi::$1Error::New(env, $2).ThrowAsJavaScriptException();\n$3return env.Null();'],
[/Nan::Throw(\w*?)Error\((.+?)\);/g, 'Napi::$1Error::New(env, $2).ThrowAsJavaScriptException();\n'],
// Nan::RangeError(error) to Napi::RangeError::New(env, error)
[/Nan::(\w*?)Error\((.+)\)/g, 'Napi::$1Error::New(env, $2)'],
[/Nan::Set\((.+?),\n* *(.+?),\n* *(.+?),\n* *(.+?)\)/g, '$1.Set($2, $3, $4)'],
[/Nan::(Escapable)?HandleScope\s+(\w+)\s*;/g, 'Napi::$1HandleScope $2(env);'],
[/Nan::(Escapable)?HandleScope/g, 'Napi::$1HandleScope'],
[/Nan::ForceSet\(([^,]+), ?/g, '$1->DefineProperty('],
[/\.ForceSet\(Napi::String::New\(env, "(\w+)"\),\s*?/g, '.DefineProperty("$1", '],
// [ /Nan::GetPropertyNames\(([^,]+)\)/, '$1->GetPropertyNames()' ],
[/Nan::Equals\(([^,]+),/g, '$1.StrictEquals('],
[/(.+)->Set\(/g, '$1.Set('],
[/Nan::Callback/g, 'Napi::FunctionReference'],
[/Nan::Persistent<Object>/g, 'Napi::ObjectReference'],
[/Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target/g, 'Napi::Env& env, Napi::Object& target'],
[/(\w+)\*\s+(\w+)\s*=\s*Nan::ObjectWrap::Unwrap<\w+>\(info\.This\(\)\);/g, '$1* $2 = this;'],
[/Nan::ObjectWrap::Unwrap<(\w+)>\((.*)\);/g, '$2.Unwrap<$1>();'],
[/Nan::NAN_METHOD_RETURN_TYPE/g, 'void'],
[/NAN_INLINE/g, 'inline'],
[/Nan::NAN_METHOD_ARGS_TYPE/g, 'const Napi::CallbackInfo&'],
[/NAN_METHOD\(([\w\d:]+?)\)/g, 'Napi::Value $1(const Napi::CallbackInfo& info)'],
[/static\s*NAN_GETTER\(([\w\d:]+?)\)/g, 'Napi::Value $1(const Napi::CallbackInfo& info)'],
[/NAN_GETTER\(([\w\d:]+?)\)/g, 'Napi::Value $1(const Napi::CallbackInfo& info)'],
[/static\s*NAN_SETTER\(([\w\d:]+?)\)/g, 'void $1(const Napi::CallbackInfo& info, const Napi::Value& value)'],
[/NAN_SETTER\(([\w\d:]+?)\)/g, 'void $1(const Napi::CallbackInfo& info, const Napi::Value& value)'],
[/void Init\((v8::)*Local<(v8::)*Object> exports\)/g, 'Napi::Object Init(Napi::Env env, Napi::Object exports)'],
[/NAN_MODULE_INIT\(([\w\d:]+?)\);/g, 'Napi::Object $1(Napi::Env env, Napi::Object exports);'],
[/NAN_MODULE_INIT\(([\w\d:]+?)\)/g, 'Napi::Object $1(Napi::Env env, Napi::Object exports)'],
[/::(Init(?:ialize)?)\(target\)/g, '::$1(env, target, module)'],
[/constructor_template/g, 'constructor'],
[/Nan::FunctionCallbackInfo<(v8::)?Value>[ ]*& [ ]*info\)[ ]*{\n*([ ]*)/gm, 'Napi::CallbackInfo& info) {\n$2Napi::Env env = info.Env();\n$2'],
[/Nan::FunctionCallbackInfo<(v8::)*Value>\s*&\s*info\);/g, 'Napi::CallbackInfo& info);'],
[/Nan::FunctionCallbackInfo<(v8::)*Value>\s*&/g, 'Napi::CallbackInfo&'],
[/Buffer::HasInstance\(([^)]+)\)/g, '$1.IsBuffer()'],
[/info\[(\d+)\]->/g, 'info[$1].'],
[/info\[([\w\d]+)\]->/g, 'info[$1].'],
[/info\.This\(\)->/g, 'info.This().'],
[/->Is(Object|String|Int32|Number)\(\)/g, '.Is$1()'],
[/info.GetReturnValue\(\).SetUndefined\(\)/g, 'return env.Undefined()'],
[/info\.GetReturnValue\(\)\.Set\(((\n|.)+?)\);/g, 'return $1;'],
// ex. Local<Value> to Napi::Value
[/v8::Local<v8::(Value|Boolean|String|Number|Object|Array|Symbol|External|Function)>/g, 'Napi::$1'],
[/Local<(Value|Boolean|String|Number|Object|Array|Symbol|External|Function)>/g, 'Napi::$1'],
// Declare an env in helper functions that take a Napi::Value
[/(\w+)\(Napi::Value (\w+)(,\s*[^()]+)?\)\s*{\n*([ ]*)/gm, '$1(Napi::Value $2$3) {\n$4Napi::Env env = $2.Env();\n$4'],
// delete #include <node.h> and/or <v8.h>
[/#include +(<|")(?:node|nan).h("|>)/g, '#include $1napi.h$2\n#include $1uv.h$2'],
// NODE_MODULE to NODE_API_MODULE
[/NODE_MODULE/g, 'NODE_API_MODULE'],
[/Nan::/g, 'Napi::'],
[/nan.h/g, 'napi.h'],
// delete .FromJust()
[/\.FromJust\(\)/g, ''],
// delete .ToLocalCheck()
[/\.ToLocalChecked\(\)/g, ''],
[/^.*->SetInternalFieldCount\(.*$/gm, ''],
// replace using node; and/or using v8; to using Napi;
[/using (node|v8);/g, 'using Napi;'],
[/using namespace (node|Nan|v8);/g, 'using namespace Napi;'],
// delete using v8::Local;
[/using v8::Local;\n/g, ''],
// replace using v8::XXX; with using Napi::XXX
[/using v8::([A-Za-z]+);/g, 'using Napi::$1;']
];
const paths = listFiles(dir);
paths.forEach(function (dirEntry) {
const filename = dirEntry.split('\\').pop().split('/').pop();
// Check whether the file is a source file or a config file
// then execute function accordingly
const sourcePattern = /.+\.h|.+\.cc|.+\.cpp/;
if (sourcePattern.test(filename)) {
convertFile(dirEntry, SourceFileOperations);
} else if (ConfigFileOperations[filename] != null) {
convertFile(dirEntry, ConfigFileOperations[filename]);
}
});
function listFiles (dir, filelist) {
const files = fs.readdirSync(dir);
filelist = filelist || [];
files.forEach(function (file) {
if (file === 'node_modules') {
return;
}
if (fs.statSync(path.join(dir, file)).isDirectory()) {
filelist = listFiles(path.join(dir, file), filelist);
} else {
filelist.push(path.join(dir, file));
}
});
return filelist;
}
function convert (content, operations) {
for (let i = 0; i < operations.length; i++) {
const operation = operations[i];
content = content.replace(operation[0], operation[1]);
}
return content;
}
function convertFile (fileName, operations) {
fs.readFile(fileName, 'utf-8', function (err, file) {
if (err) throw err;
file = convert(file, operations);
fs.writeFile(fileName, file, function (err) {
if (err) throw err;
});
});
}

View File

@ -0,0 +1,79 @@
#!/usr/bin/env node
const spawn = require('child_process').spawnSync;
const filesToCheck = '*.js';
const FORMAT_START = process.env.FORMAT_START || 'main';
const IS_WIN = process.platform === 'win32';
const ESLINT_PATH = IS_WIN ? 'node_modules\\.bin\\eslint.cmd' : 'node_modules/.bin/eslint';
function main (args) {
let fix = false;
while (args.length > 0) {
switch (args[0]) {
case '-f':
case '--fix':
fix = true;
break;
default:
}
args.shift();
}
// Check js files that change on unstaged file
const fileUnStaged = spawn(
'git',
['diff', '--name-only', '--diff-filter=d', FORMAT_START, filesToCheck],
{
encoding: 'utf-8'
}
);
// Check js files that change on staged file
const fileStaged = spawn(
'git',
['diff', '--name-only', '--cached', '--diff-filter=d', FORMAT_START, filesToCheck],
{
encoding: 'utf-8'
}
);
const options = [
...fileStaged.stdout.split('\n').filter((f) => f !== ''),
...fileUnStaged.stdout.split('\n').filter((f) => f !== '')
];
if (fix) {
options.push('--fix');
}
const result = spawn(ESLINT_PATH, [...options], {
encoding: 'utf-8'
});
if (result.error && result.error.errno === 'ENOENT') {
console.error('Eslint not found! Eslint is supposed to be found at ', ESLINT_PATH);
return 2;
}
if (result.status === 1) {
console.error('Eslint error:', result.stdout);
const fixCmd = 'npm run lint:fix';
console.error(`ERROR: please run "${fixCmd}" to format changes in your commit
Note that when running the command locally, please keep your local
main branch and working branch up to date with nodejs/node-addon-api
to exclude un-related complains.
Or you can run "env FORMAT_START=upstream/main ${fixCmd}".
Also fix JS files by yourself if necessary.`);
return 1;
}
if (result.stderr) {
console.error('Error running eslint:', result.stderr);
return 2;
}
}
if (require.main === module) {
process.exitCode = main(process.argv.slice(2));
}

View File

@ -1,127 +0,0 @@
# normalize-path [![NPM version](https://img.shields.io/npm/v/normalize-path.svg?style=flat)](https://www.npmjs.com/package/normalize-path) [![NPM monthly downloads](https://img.shields.io/npm/dm/normalize-path.svg?style=flat)](https://npmjs.org/package/normalize-path) [![NPM total downloads](https://img.shields.io/npm/dt/normalize-path.svg?style=flat)](https://npmjs.org/package/normalize-path) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/normalize-path.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/normalize-path)
> Normalize slashes in a file path to be posix/unix-like forward slashes. Also condenses repeat slashes to a single slash and removes and trailing slashes, unless disabled.
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save normalize-path
```
## Usage
```js
const normalize = require('normalize-path');
console.log(normalize('\\foo\\bar\\baz\\'));
//=> '/foo/bar/baz'
```
**win32 namespaces**
```js
console.log(normalize('\\\\?\\UNC\\Server01\\user\\docs\\Letter.txt'));
//=> '//?/UNC/Server01/user/docs/Letter.txt'
console.log(normalize('\\\\.\\CdRomX'));
//=> '//./CdRomX'
```
**Consecutive slashes**
Condenses multiple consecutive forward slashes (except for leading slashes in win32 namespaces) to a single slash.
```js
console.log(normalize('.//foo//bar///////baz/'));
//=> './foo/bar/baz'
```
### Trailing slashes
By default trailing slashes are removed. Pass `false` as the last argument to disable this behavior and _**keep** trailing slashes_:
```js
console.log(normalize('foo\\bar\\baz\\', false)); //=> 'foo/bar/baz/'
console.log(normalize('./foo/bar/baz/', false)); //=> './foo/bar/baz/'
```
## Release history
### v3.0
No breaking changes in this release.
* a check was added to ensure that [win32 namespaces](https://msdn.microsoft.com/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces) are handled properly by win32 `path.parse()` after a path has been normalized by this library.
* a minor optimization was made to simplify how the trailing separator was handled
## About
<details>
<summary><strong>Contributing</strong></summary>
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
</details>
<details>
<summary><strong>Running Tests</strong></summary>
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install && npm test
```
</details>
<details>
<summary><strong>Building docs</strong></summary>
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```
</details>
### Related projects
Other useful path-related libraries:
* [contains-path](https://www.npmjs.com/package/contains-path): Return true if a file path contains the given path. | [homepage](https://github.com/jonschlinkert/contains-path "Return true if a file path contains the given path.")
* [is-absolute](https://www.npmjs.com/package/is-absolute): Returns true if a file path is absolute. Does not rely on the path module… [more](https://github.com/jonschlinkert/is-absolute) | [homepage](https://github.com/jonschlinkert/is-absolute "Returns true if a file path is absolute. Does not rely on the path module and can be used as a polyfill for node.js native `path.isAbolute`.")
* [is-relative](https://www.npmjs.com/package/is-relative): Returns `true` if the path appears to be relative. | [homepage](https://github.com/jonschlinkert/is-relative "Returns `true` if the path appears to be relative.")
* [parse-filepath](https://www.npmjs.com/package/parse-filepath): Pollyfill for node.js `path.parse`, parses a filepath into an object. | [homepage](https://github.com/jonschlinkert/parse-filepath "Pollyfill for node.js `path.parse`, parses a filepath into an object.")
* [path-ends-with](https://www.npmjs.com/package/path-ends-with): Return `true` if a file path ends with the given string/suffix. | [homepage](https://github.com/jonschlinkert/path-ends-with "Return `true` if a file path ends with the given string/suffix.")
* [unixify](https://www.npmjs.com/package/unixify): Convert Windows file paths to unix paths. | [homepage](https://github.com/jonschlinkert/unixify "Convert Windows file paths to unix paths.")
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 35 | [jonschlinkert](https://github.com/jonschlinkert) |
| 1 | [phated](https://github.com/phated) |
### Author
**Jon Schlinkert**
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
### License
Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on April 19, 2018._

View File

@ -1,35 +0,0 @@
/*!
* normalize-path <https://github.com/jonschlinkert/normalize-path>
*
* Copyright (c) 2014-2018, Jon Schlinkert.
* Released under the MIT License.
*/
module.exports = function(path, stripTrailing) {
if (typeof path !== 'string') {
throw new TypeError('expected path to be a string');
}
if (path === '\\' || path === '/') return '/';
var len = path.length;
if (len <= 1) return path;
// ensure that win32 namespaces has two leading slashes, so that the path is
// handled properly by the win32 version of path.parse() after being normalized
// https://msdn.microsoft.com/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces
var prefix = '';
if (len > 4 && path[3] === '\\') {
var ch = path[2];
if ((ch === '?' || ch === '.') && path.slice(0, 2) === '\\\\') {
path = path.slice(2);
prefix = '//';
}
}
var segs = path.split(/[/\\]+/);
if (stripTrailing !== false && segs[segs.length - 1] === '') {
segs.pop();
}
return prefix + segs.join('/');
};

View File

@ -1,77 +0,0 @@
{
"name": "normalize-path",
"description": "Normalize slashes in a file path to be posix/unix-like forward slashes. Also condenses repeat slashes to a single slash and removes and trailing slashes, unless disabled.",
"version": "3.0.0",
"homepage": "https://github.com/jonschlinkert/normalize-path",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
"Blaine Bublitz (https://twitter.com/BlaineBublitz)",
"Jon Schlinkert (http://twitter.com/jonschlinkert)"
],
"repository": "jonschlinkert/normalize-path",
"bugs": {
"url": "https://github.com/jonschlinkert/normalize-path/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"devDependencies": {
"gulp-format-md": "^1.0.0",
"minimist": "^1.2.0",
"mocha": "^3.5.3"
},
"keywords": [
"absolute",
"backslash",
"delimiter",
"file",
"file-path",
"filepath",
"fix",
"forward",
"fp",
"fs",
"normalize",
"path",
"relative",
"separator",
"slash",
"slashes",
"trailing",
"unix",
"urix"
],
"verb": {
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"related": {
"description": "Other useful path-related libraries:",
"list": [
"contains-path",
"is-absolute",
"is-relative",
"parse-filepath",
"path-ends-with",
"path-ends-with",
"unixify"
]
},
"lint": {
"reflinks": true
}
}
}

Some files were not shown because too many files have changed in this diff Show More