latest
This commit is contained in:
parent
eb0d79d503
commit
b03c1d4619
65
.env
Normal file
65
.env
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
APP_NAME="Smart Car Park"
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=base64:92pMpQ2HV7icz1lfUZUQfUguPfUdqYUPVU+aWdYDstY=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
APP_LOCALE=en
|
||||||
|
APP_FALLBACK_LOCALE=en
|
||||||
|
APP_FAKER_LOCALE=en_US
|
||||||
|
|
||||||
|
APP_MAINTENANCE_DRIVER=file
|
||||||
|
# APP_MAINTENANCE_STORE=database
|
||||||
|
|
||||||
|
# PHP_CLI_SERVER_WORKERS=4
|
||||||
|
|
||||||
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_STACK=single
|
||||||
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=sqlite
|
||||||
|
# DB_HOST=127.0.0.1
|
||||||
|
# DB_PORT=3306
|
||||||
|
# DB_DATABASE=laravel
|
||||||
|
# DB_USERNAME=root
|
||||||
|
# DB_PASSWORD=
|
||||||
|
|
||||||
|
SESSION_DRIVER=database
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
SESSION_ENCRYPT=false
|
||||||
|
SESSION_PATH=/
|
||||||
|
SESSION_DOMAIN=null
|
||||||
|
|
||||||
|
BROADCAST_CONNECTION=log
|
||||||
|
FILESYSTEM_DISK=local
|
||||||
|
QUEUE_CONNECTION=database
|
||||||
|
|
||||||
|
CACHE_STORE=database
|
||||||
|
# CACHE_PREFIX=
|
||||||
|
|
||||||
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
|
REDIS_CLIENT=phpredis
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=log
|
||||||
|
MAIL_SCHEME=null
|
||||||
|
MAIL_HOST=127.0.0.1
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
48
.gitignore
vendored
48
.gitignore
vendored
@ -1,24 +1,24 @@
|
|||||||
*.log
|
# *.log
|
||||||
.DS_Store
|
# .DS_Store
|
||||||
.env
|
# .env
|
||||||
.env.backup
|
# .env.backup
|
||||||
.env.production
|
# .env.production
|
||||||
.phpactor.json
|
# .phpactor.json
|
||||||
.phpunit.result.cache
|
# .phpunit.result.cache
|
||||||
/.fleet
|
# /.fleet
|
||||||
/.idea
|
# /.idea
|
||||||
/.nova
|
# /.nova
|
||||||
/.phpunit.cache
|
# /.phpunit.cache
|
||||||
/.vscode
|
# /.vscode
|
||||||
/.zed
|
# /.zed
|
||||||
/auth.json
|
# /auth.json
|
||||||
/node_modules
|
# /node_modules
|
||||||
/public/build
|
# /public/build
|
||||||
/public/hot
|
# /public/hot
|
||||||
/public/storage
|
# /public/storage
|
||||||
/storage/*.key
|
# /storage/*.key
|
||||||
/storage/pail
|
# /storage/pail
|
||||||
/vendor
|
# /vendor
|
||||||
Homestead.json
|
# Homestead.json
|
||||||
Homestead.yaml
|
# Homestead.yaml
|
||||||
Thumbs.db
|
# Thumbs.db
|
||||||
|
|||||||
16
node_modules/.bin/clean-orphaned-assets
generated
vendored
Normal file
16
node_modules/.bin/clean-orphaned-assets
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../laravel-vite-plugin/bin/clean.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../laravel-vite-plugin/bin/clean.js" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/clean-orphaned-assets.cmd
generated
vendored
Normal file
17
node_modules/.bin/clean-orphaned-assets.cmd
generated
vendored
Normal 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%\..\laravel-vite-plugin\bin\clean.js" %*
|
||||||
28
node_modules/.bin/clean-orphaned-assets.ps1
generated
vendored
Normal file
28
node_modules/.bin/clean-orphaned-assets.ps1
generated
vendored
Normal 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/../laravel-vite-plugin/bin/clean.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../laravel-vite-plugin/bin/clean.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../laravel-vite-plugin/bin/clean.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../laravel-vite-plugin/bin/clean.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
16
node_modules/.bin/conc
generated
vendored
Normal file
16
node_modules/.bin/conc
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../concurrently/dist/bin/concurrently.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../concurrently/dist/bin/concurrently.js" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/conc.cmd
generated
vendored
Normal file
17
node_modules/.bin/conc.cmd
generated
vendored
Normal 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%\..\concurrently\dist\bin\concurrently.js" %*
|
||||||
28
node_modules/.bin/conc.ps1
generated
vendored
Normal file
28
node_modules/.bin/conc.ps1
generated
vendored
Normal 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/../concurrently/dist/bin/concurrently.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../concurrently/dist/bin/concurrently.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../concurrently/dist/bin/concurrently.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../concurrently/dist/bin/concurrently.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
16
node_modules/.bin/concurrently
generated
vendored
Normal file
16
node_modules/.bin/concurrently
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../concurrently/dist/bin/concurrently.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../concurrently/dist/bin/concurrently.js" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/concurrently.cmd
generated
vendored
Normal file
17
node_modules/.bin/concurrently.cmd
generated
vendored
Normal 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%\..\concurrently\dist\bin\concurrently.js" %*
|
||||||
28
node_modules/.bin/concurrently.ps1
generated
vendored
Normal file
28
node_modules/.bin/concurrently.ps1
generated
vendored
Normal 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/../concurrently/dist/bin/concurrently.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../concurrently/dist/bin/concurrently.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../concurrently/dist/bin/concurrently.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../concurrently/dist/bin/concurrently.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
16
node_modules/.bin/esbuild
generated
vendored
Normal file
16
node_modules/.bin/esbuild
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../esbuild/bin/esbuild" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../esbuild/bin/esbuild" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/esbuild.cmd
generated
vendored
Normal file
17
node_modules/.bin/esbuild.cmd
generated
vendored
Normal 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%\..\esbuild\bin\esbuild" %*
|
||||||
28
node_modules/.bin/esbuild.ps1
generated
vendored
Normal file
28
node_modules/.bin/esbuild.ps1
generated
vendored
Normal 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/../esbuild/bin/esbuild" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
16
node_modules/.bin/nanoid
generated
vendored
Normal file
16
node_modules/.bin/nanoid
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../nanoid/bin/nanoid.cjs" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../nanoid/bin/nanoid.cjs" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/nanoid.cmd
generated
vendored
Normal file
17
node_modules/.bin/nanoid.cmd
generated
vendored
Normal 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%\..\nanoid\bin\nanoid.cjs" %*
|
||||||
28
node_modules/.bin/nanoid.ps1
generated
vendored
Normal file
28
node_modules/.bin/nanoid.ps1
generated
vendored
Normal 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/../nanoid/bin/nanoid.cjs" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
16
node_modules/.bin/rollup
generated
vendored
Normal file
16
node_modules/.bin/rollup
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../rollup/dist/bin/rollup" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../rollup/dist/bin/rollup" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/rollup.cmd
generated
vendored
Normal file
17
node_modules/.bin/rollup.cmd
generated
vendored
Normal 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%\..\rollup\dist\bin\rollup" %*
|
||||||
28
node_modules/.bin/rollup.ps1
generated
vendored
Normal file
28
node_modules/.bin/rollup.ps1
generated
vendored
Normal 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/../rollup/dist/bin/rollup" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
16
node_modules/.bin/sass
generated
vendored
Normal file
16
node_modules/.bin/sass
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../sass/sass.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../sass/sass.js" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/sass.cmd
generated
vendored
Normal file
17
node_modules/.bin/sass.cmd
generated
vendored
Normal 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%\..\sass\sass.js" %*
|
||||||
28
node_modules/.bin/sass.ps1
generated
vendored
Normal file
28
node_modules/.bin/sass.ps1
generated
vendored
Normal 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/../sass/sass.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../sass/sass.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../sass/sass.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../sass/sass.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
16
node_modules/.bin/tree-kill
generated
vendored
Normal file
16
node_modules/.bin/tree-kill
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../tree-kill/cli.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../tree-kill/cli.js" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/tree-kill.cmd
generated
vendored
Normal file
17
node_modules/.bin/tree-kill.cmd
generated
vendored
Normal 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%\..\tree-kill\cli.js" %*
|
||||||
28
node_modules/.bin/tree-kill.ps1
generated
vendored
Normal file
28
node_modules/.bin/tree-kill.ps1
generated
vendored
Normal 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/../tree-kill/cli.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../tree-kill/cli.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../tree-kill/cli.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../tree-kill/cli.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
16
node_modules/.bin/vite
generated
vendored
Normal file
16
node_modules/.bin/vite
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../vite/bin/vite.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../vite/bin/vite.js" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/vite.cmd
generated
vendored
Normal file
17
node_modules/.bin/vite.cmd
generated
vendored
Normal 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%\..\vite\bin\vite.js" %*
|
||||||
28
node_modules/.bin/vite.ps1
generated
vendored
Normal file
28
node_modules/.bin/vite.ps1
generated
vendored
Normal 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/../vite/bin/vite.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
1212
node_modules/.package-lock.json
generated
vendored
Normal file
1212
node_modules/.package-lock.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
25
node_modules/.vite/deps/_metadata.json
generated
vendored
Normal file
25
node_modules/.vite/deps/_metadata.json
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"hash": "5d343328",
|
||||||
|
"configHash": "ed1822e2",
|
||||||
|
"lockfileHash": "3efeb88e",
|
||||||
|
"browserHash": "935c720e",
|
||||||
|
"optimized": {
|
||||||
|
"axios": {
|
||||||
|
"src": "../../axios/index.js",
|
||||||
|
"file": "axios.js",
|
||||||
|
"fileHash": "ed6e5af8",
|
||||||
|
"needsInterop": false
|
||||||
|
},
|
||||||
|
"bootstrap": {
|
||||||
|
"src": "../../bootstrap/dist/js/bootstrap.esm.js",
|
||||||
|
"file": "bootstrap.js",
|
||||||
|
"fileHash": "e1cdcbb2",
|
||||||
|
"needsInterop": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chunks": {
|
||||||
|
"chunk-PZ5AY32C": {
|
||||||
|
"file": "chunk-PZ5AY32C.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2789
node_modules/.vite/deps/axios.js
generated
vendored
Normal file
2789
node_modules/.vite/deps/axios.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
node_modules/.vite/deps/axios.js.map
generated
vendored
Normal file
7
node_modules/.vite/deps/axios.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5177
node_modules/.vite/deps/bootstrap.js
generated
vendored
Normal file
5177
node_modules/.vite/deps/bootstrap.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
node_modules/.vite/deps/bootstrap.js.map
generated
vendored
Normal file
7
node_modules/.vite/deps/bootstrap.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
9
node_modules/.vite/deps/chunk-PZ5AY32C.js
generated
vendored
Normal file
9
node_modules/.vite/deps/chunk-PZ5AY32C.js
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
var __defProp = Object.defineProperty;
|
||||||
|
var __export = (target, all) => {
|
||||||
|
for (var name in all)
|
||||||
|
__defProp(target, name, { get: all[name], enumerable: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
__export
|
||||||
|
};
|
||||||
7
node_modules/.vite/deps/chunk-PZ5AY32C.js.map
generated
vendored
Normal file
7
node_modules/.vite/deps/chunk-PZ5AY32C.js.map
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"sources": [],
|
||||||
|
"sourcesContent": [],
|
||||||
|
"mappings": "",
|
||||||
|
"names": []
|
||||||
|
}
|
||||||
3
node_modules/.vite/deps/package.json
generated
vendored
Normal file
3
node_modules/.vite/deps/package.json
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
3
node_modules/@esbuild/win32-x64/README.md
generated
vendored
Normal file
3
node_modules/@esbuild/win32-x64/README.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# esbuild
|
||||||
|
|
||||||
|
This is the Windows 64-bit binary for esbuild, a JavaScript bundler and minifier. See https://github.com/evanw/esbuild for details.
|
||||||
BIN
node_modules/@esbuild/win32-x64/esbuild.exe
generated
vendored
Normal file
BIN
node_modules/@esbuild/win32-x64/esbuild.exe
generated
vendored
Normal file
Binary file not shown.
20
node_modules/@esbuild/win32-x64/package.json
generated
vendored
Normal file
20
node_modules/@esbuild/win32-x64/package.json
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "@esbuild/win32-x64",
|
||||||
|
"version": "0.27.7",
|
||||||
|
"description": "The Windows 64-bit binary for esbuild, a JavaScript bundler.",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/evanw/esbuild.git"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
|
}
|
||||||
21
node_modules/@parcel/watcher-win32-x64/LICENSE
generated
vendored
Normal file
21
node_modules/@parcel/watcher-win32-x64/LICENSE
generated
vendored
Normal 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.
|
||||||
1
node_modules/@parcel/watcher-win32-x64/README.md
generated
vendored
Normal file
1
node_modules/@parcel/watcher-win32-x64/README.md
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
This is the win32-x64 build of @parcel/watcher. See https://github.com/parcel-bundler/watcher for details.
|
||||||
30
node_modules/@parcel/watcher-win32-x64/package.json
generated
vendored
Normal file
30
node_modules/@parcel/watcher-win32-x64/package.json
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "@parcel/watcher-win32-x64",
|
||||||
|
"version": "2.5.6",
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
node_modules/@parcel/watcher-win32-x64/watcher.node
generated
vendored
Normal file
BIN
node_modules/@parcel/watcher-win32-x64/watcher.node
generated
vendored
Normal file
Binary file not shown.
21
node_modules/@parcel/watcher/LICENSE
generated
vendored
Normal file
21
node_modules/@parcel/watcher/LICENSE
generated
vendored
Normal 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.
|
||||||
136
node_modules/@parcel/watcher/README.md
generated
vendored
Normal file
136
node_modules/@parcel/watcher/README.md
generated
vendored
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# @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 [`picomatch`](https://github.com/micromatch/picomatch) (see [features](https://github.com/micromatch/picomatch#globbing-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)
|
||||||
|
- [Meteor](https://github.com/meteor/meteor)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
109
node_modules/@parcel/watcher/binding.gyp
generated
vendored
Normal file
109
node_modules/@parcel/watcher/binding.gyp
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"target_name": "watcher",
|
||||||
|
"defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
|
||||||
|
"sources": [ "src/binding.cc", "src/Watcher.cc", "src/Backend.cc", "src/DirTree.cc", "src/Glob.cc", "src/Debounce.cc" ],
|
||||||
|
"include_dirs" : ["<!(node -p \"require('node-addon-api').include_dir\")"],
|
||||||
|
'cflags!': [ '-fno-exceptions', '-std=c++17' ],
|
||||||
|
'cflags_cc!': [ '-fno-exceptions', '-std=c++17' ],
|
||||||
|
'cflags': [ '-fstack-protector-strong' ],
|
||||||
|
"conditions": [
|
||||||
|
['OS=="mac"', {
|
||||||
|
"sources": [
|
||||||
|
"src/watchman/BSER.cc",
|
||||||
|
"src/watchman/WatchmanBackend.cc",
|
||||||
|
"src/shared/BruteForceBackend.cc",
|
||||||
|
"src/unix/fts.cc",
|
||||||
|
"src/macos/FSEventsBackend.cc",
|
||||||
|
"src/kqueue/KqueueBackend.cc"
|
||||||
|
],
|
||||||
|
"link_settings": {
|
||||||
|
"libraries": ["CoreServices.framework"]
|
||||||
|
},
|
||||||
|
"defines": [
|
||||||
|
"WATCHMAN",
|
||||||
|
"BRUTE_FORCE",
|
||||||
|
"FS_EVENTS",
|
||||||
|
"KQUEUE"
|
||||||
|
],
|
||||||
|
"xcode_settings": {
|
||||||
|
"GCC_ENABLE_CPP_EXCEPTIONS": "YES"
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
['OS=="mac" and target_arch=="arm64"', {
|
||||||
|
"xcode_settings": {
|
||||||
|
"ARCHS": ["arm64"]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
['OS=="linux" or OS=="android"', {
|
||||||
|
"sources": [
|
||||||
|
"src/watchman/BSER.cc",
|
||||||
|
"src/watchman/WatchmanBackend.cc",
|
||||||
|
"src/shared/BruteForceBackend.cc",
|
||||||
|
"src/linux/InotifyBackend.cc",
|
||||||
|
"src/unix/legacy.cc"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"WATCHMAN",
|
||||||
|
"INOTIFY",
|
||||||
|
"BRUTE_FORCE"
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
['OS=="win"', {
|
||||||
|
"sources": [
|
||||||
|
"src/watchman/BSER.cc",
|
||||||
|
"src/watchman/WatchmanBackend.cc",
|
||||||
|
"src/shared/BruteForceBackend.cc",
|
||||||
|
"src/windows/WindowsBackend.cc",
|
||||||
|
"src/windows/win_utils.cc"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"WATCHMAN",
|
||||||
|
"WINDOWS",
|
||||||
|
"BRUTE_FORCE"
|
||||||
|
],
|
||||||
|
"msvs_settings": {
|
||||||
|
"VCCLCompilerTool": {
|
||||||
|
"ExceptionHandling": 1, # /EHsc
|
||||||
|
"AdditionalOptions": [
|
||||||
|
"-std:c++17",
|
||||||
|
"/guard:cf",
|
||||||
|
"/W3",
|
||||||
|
"/we4146",
|
||||||
|
"/w34244",
|
||||||
|
"/we4267",
|
||||||
|
"/sdl",
|
||||||
|
"/ZH:SHA_256"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"VCLinkerTool": {
|
||||||
|
"AdditionalOptions": [
|
||||||
|
"/DYNAMICBASE",
|
||||||
|
"/guard:cf"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
['OS=="freebsd"', {
|
||||||
|
"sources": [
|
||||||
|
"src/watchman/BSER.cc",
|
||||||
|
"src/watchman/WatchmanBackend.cc",
|
||||||
|
"src/shared/BruteForceBackend.cc",
|
||||||
|
"src/unix/fts.cc",
|
||||||
|
"src/kqueue/KqueueBackend.cc"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"WATCHMAN",
|
||||||
|
"BRUTE_FORCE",
|
||||||
|
"KQUEUE"
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variables": {
|
||||||
|
"openssl_fips": "",
|
||||||
|
"node_use_dtrace": "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
49
node_modules/@parcel/watcher/index.d.ts
generated
vendored
Normal file
49
node_modules/@parcel/watcher/index.d.ts
generated
vendored
Normal 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;
|
||||||
42
node_modules/@parcel/watcher/index.js
generated
vendored
Normal file
42
node_modules/@parcel/watcher/index.js
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const {createWrapper} = require('./wrapper');
|
||||||
|
|
||||||
|
let name = `@parcel/watcher-${process.platform}-${process.arch}`;
|
||||||
|
if (process.platform === 'linux') {
|
||||||
|
const { MUSL, familySync } = require('detect-libc');
|
||||||
|
const family = familySync();
|
||||||
|
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;
|
||||||
48
node_modules/@parcel/watcher/index.js.flow
generated
vendored
Normal file
48
node_modules/@parcel/watcher/index.js.flow
generated
vendored
Normal 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>
|
||||||
|
}
|
||||||
88
node_modules/@parcel/watcher/package.json
generated
vendored
Normal file
88
node_modules/@parcel/watcher/package.json
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"name": "@parcel/watcher",
|
||||||
|
"version": "2.5.6",
|
||||||
|
"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",
|
||||||
|
"src",
|
||||||
|
"scripts/build-from-source.js",
|
||||||
|
"binding.gyp"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"prebuild": "prebuildify --napi --strip --tag-libc",
|
||||||
|
"format": "prettier --write \"./**/*.{js,json,md}\"",
|
||||||
|
"build": "node-gyp rebuild",
|
||||||
|
"install": "node scripts/build-from-source.js",
|
||||||
|
"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": "^2.0.3",
|
||||||
|
"is-glob": "^4.0.3",
|
||||||
|
"node-addon-api": "^7.0.0",
|
||||||
|
"picomatch": "^4.0.3"
|
||||||
|
},
|
||||||
|
"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": "^6.0.1",
|
||||||
|
"prettier": "^2.3.2"
|
||||||
|
},
|
||||||
|
"binary": {
|
||||||
|
"napi_versions": [
|
||||||
|
3
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@parcel/watcher-darwin-x64": "2.5.6",
|
||||||
|
"@parcel/watcher-darwin-arm64": "2.5.6",
|
||||||
|
"@parcel/watcher-win32-x64": "2.5.6",
|
||||||
|
"@parcel/watcher-win32-arm64": "2.5.6",
|
||||||
|
"@parcel/watcher-win32-ia32": "2.5.6",
|
||||||
|
"@parcel/watcher-linux-x64-glibc": "2.5.6",
|
||||||
|
"@parcel/watcher-linux-x64-musl": "2.5.6",
|
||||||
|
"@parcel/watcher-linux-arm64-glibc": "2.5.6",
|
||||||
|
"@parcel/watcher-linux-arm64-musl": "2.5.6",
|
||||||
|
"@parcel/watcher-linux-arm-glibc": "2.5.6",
|
||||||
|
"@parcel/watcher-linux-arm-musl": "2.5.6",
|
||||||
|
"@parcel/watcher-android-arm64": "2.5.6",
|
||||||
|
"@parcel/watcher-freebsd-x64": "2.5.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
node_modules/@parcel/watcher/scripts/build-from-source.js
generated
vendored
Normal file
13
node_modules/@parcel/watcher/scripts/build-from-source.js
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {spawn} = require('child_process');
|
||||||
|
|
||||||
|
if (process.env.npm_config_build_from_source === 'true') {
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
|
||||||
|
function build() {
|
||||||
|
spawn('node-gyp', ['rebuild'], { stdio: 'inherit', shell: true }).on('exit', function (code) {
|
||||||
|
process.exit(code);
|
||||||
|
});
|
||||||
|
}
|
||||||
186
node_modules/@parcel/watcher/src/Backend.cc
generated
vendored
Normal file
186
node_modules/@parcel/watcher/src/Backend.cc
generated
vendored
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#ifdef FS_EVENTS
|
||||||
|
#include "macos/FSEventsBackend.hh"
|
||||||
|
#endif
|
||||||
|
#ifdef WATCHMAN
|
||||||
|
#include "watchman/WatchmanBackend.hh"
|
||||||
|
#endif
|
||||||
|
#ifdef WINDOWS
|
||||||
|
#include "windows/WindowsBackend.hh"
|
||||||
|
#endif
|
||||||
|
#ifdef INOTIFY
|
||||||
|
#include "linux/InotifyBackend.hh"
|
||||||
|
#endif
|
||||||
|
#ifdef KQUEUE
|
||||||
|
#include "kqueue/KqueueBackend.hh"
|
||||||
|
#endif
|
||||||
|
#ifdef __wasm32__
|
||||||
|
#include "wasm/WasmBackend.hh"
|
||||||
|
#endif
|
||||||
|
#include "shared/BruteForceBackend.hh"
|
||||||
|
|
||||||
|
#include "Backend.hh"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, std::shared_ptr<Backend>>& getSharedBackends() {
|
||||||
|
static std::unordered_map<std::string, std::shared_ptr<Backend>>* sharedBackends =
|
||||||
|
new std::unordered_map<std::string, std::shared_ptr<Backend>>();
|
||||||
|
return *sharedBackends;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Backend> getBackend(std::string backend) {
|
||||||
|
// Use FSEvents on macOS by default.
|
||||||
|
// Use watchman by default if available on other platforms.
|
||||||
|
// Fall back to brute force.
|
||||||
|
#ifdef FS_EVENTS
|
||||||
|
if (backend == "fs-events" || backend == "default") {
|
||||||
|
return std::make_shared<FSEventsBackend>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WATCHMAN
|
||||||
|
if ((backend == "watchman" || backend == "default") && WatchmanBackend::checkAvailable()) {
|
||||||
|
return std::make_shared<WatchmanBackend>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WINDOWS
|
||||||
|
if (backend == "windows" || backend == "default") {
|
||||||
|
return std::make_shared<WindowsBackend>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef INOTIFY
|
||||||
|
if (backend == "inotify" || backend == "default") {
|
||||||
|
return std::make_shared<InotifyBackend>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef KQUEUE
|
||||||
|
if (backend == "kqueue" || backend == "default") {
|
||||||
|
return std::make_shared<KqueueBackend>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef __wasm32__
|
||||||
|
if (backend == "wasm" || backend == "default") {
|
||||||
|
return std::make_shared<WasmBackend>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (backend == "brute-force" || backend == "default") {
|
||||||
|
return std::make_shared<BruteForceBackend>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Backend> Backend::getShared(std::string backend) {
|
||||||
|
auto found = getSharedBackends().find(backend);
|
||||||
|
if (found != getSharedBackends().end()) {
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = getBackend(backend);
|
||||||
|
if (!result) {
|
||||||
|
return getShared("default");
|
||||||
|
}
|
||||||
|
|
||||||
|
result->run();
|
||||||
|
getSharedBackends().emplace(backend, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeShared(Backend *backend) {
|
||||||
|
for (auto it = getSharedBackends().begin(); it != getSharedBackends().end(); it++) {
|
||||||
|
if (it->second.get() == backend) {
|
||||||
|
getSharedBackends().erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free up memory.
|
||||||
|
if (getSharedBackends().size() == 0) {
|
||||||
|
getSharedBackends().rehash(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::run() {
|
||||||
|
#ifndef __wasm32__
|
||||||
|
mThread = std::thread([this] () {
|
||||||
|
try {
|
||||||
|
start();
|
||||||
|
} catch (std::exception &err) {
|
||||||
|
handleError(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mThread.joinable()) {
|
||||||
|
mStartedSignal.wait();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
try {
|
||||||
|
start();
|
||||||
|
} catch (std::exception &err) {
|
||||||
|
handleError(err);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::notifyStarted() {
|
||||||
|
mStartedSignal.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::start() {
|
||||||
|
notifyStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
Backend::~Backend() {
|
||||||
|
#ifndef __wasm32__
|
||||||
|
// Wait for thread to stop
|
||||||
|
if (mThread.joinable()) {
|
||||||
|
// If the backend is being destroyed from the thread itself, detach, otherwise join.
|
||||||
|
if (mThread.get_id() == std::this_thread::get_id()) {
|
||||||
|
mThread.detach();
|
||||||
|
} else {
|
||||||
|
mThread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::watch(WatcherRef watcher) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
auto res = mSubscriptions.find(watcher);
|
||||||
|
if (res == mSubscriptions.end()) {
|
||||||
|
try {
|
||||||
|
this->subscribe(watcher);
|
||||||
|
mSubscriptions.insert(watcher);
|
||||||
|
} catch (std::exception&) {
|
||||||
|
unref();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::unwatch(WatcherRef watcher) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
size_t deleted = mSubscriptions.erase(watcher);
|
||||||
|
if (deleted > 0) {
|
||||||
|
this->unsubscribe(watcher);
|
||||||
|
unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::unref() {
|
||||||
|
if (mSubscriptions.size() == 0) {
|
||||||
|
removeShared(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::handleWatcherError(WatcherError &err) {
|
||||||
|
unwatch(err.mWatcher);
|
||||||
|
err.mWatcher->notifyError(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::handleError(std::exception &err) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
for (auto it = mSubscriptions.begin(); it != mSubscriptions.end(); it++) {
|
||||||
|
(*it)->notifyError(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeShared(this);
|
||||||
|
}
|
||||||
37
node_modules/@parcel/watcher/src/Backend.hh
generated
vendored
Normal file
37
node_modules/@parcel/watcher/src/Backend.hh
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef BACKEND_H
|
||||||
|
#define BACKEND_H
|
||||||
|
|
||||||
|
#include "Event.hh"
|
||||||
|
#include "Watcher.hh"
|
||||||
|
#include "Signal.hh"
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class Backend {
|
||||||
|
public:
|
||||||
|
virtual ~Backend();
|
||||||
|
void run();
|
||||||
|
void notifyStarted();
|
||||||
|
|
||||||
|
virtual void start();
|
||||||
|
virtual void writeSnapshot(WatcherRef watcher, std::string *snapshotPath) = 0;
|
||||||
|
virtual void getEventsSince(WatcherRef watcher, std::string *snapshotPath) = 0;
|
||||||
|
virtual void subscribe(WatcherRef watcher) = 0;
|
||||||
|
virtual void unsubscribe(WatcherRef watcher) = 0;
|
||||||
|
|
||||||
|
static std::shared_ptr<Backend> getShared(std::string backend);
|
||||||
|
|
||||||
|
void watch(WatcherRef watcher);
|
||||||
|
void unwatch(WatcherRef watcher);
|
||||||
|
void unref();
|
||||||
|
void handleWatcherError(WatcherError &err);
|
||||||
|
|
||||||
|
std::mutex mMutex;
|
||||||
|
std::thread mThread;
|
||||||
|
private:
|
||||||
|
std::unordered_set<WatcherRef> mSubscriptions;
|
||||||
|
Signal mStartedSignal;
|
||||||
|
|
||||||
|
void handleError(std::exception &err);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
113
node_modules/@parcel/watcher/src/Debounce.cc
generated
vendored
Normal file
113
node_modules/@parcel/watcher/src/Debounce.cc
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include "Debounce.hh"
|
||||||
|
|
||||||
|
#ifdef __wasm32__
|
||||||
|
extern "C" void on_timeout(void *ctx) {
|
||||||
|
Debounce *debounce = (Debounce *)ctx;
|
||||||
|
debounce->notify();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::shared_ptr<Debounce> Debounce::getShared() {
|
||||||
|
static std::weak_ptr<Debounce> sharedInstance;
|
||||||
|
std::shared_ptr<Debounce> shared = sharedInstance.lock();
|
||||||
|
if (!shared) {
|
||||||
|
shared = std::make_shared<Debounce>();
|
||||||
|
sharedInstance = shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debounce::Debounce() {
|
||||||
|
mRunning = true;
|
||||||
|
#ifndef __wasm32__
|
||||||
|
mThread = std::thread([this] () {
|
||||||
|
loop();
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Debounce::~Debounce() {
|
||||||
|
mRunning = false;
|
||||||
|
#ifndef __wasm32__
|
||||||
|
mWaitSignal.notify();
|
||||||
|
mThread.join();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debounce::add(void *key, std::function<void()> cb) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mCallbacks.emplace(key, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debounce::remove(void *key) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mCallbacks.erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debounce::trigger() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
#ifdef __wasm32__
|
||||||
|
notifyIfReady();
|
||||||
|
#else
|
||||||
|
mWaitSignal.notify();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __wasm32__
|
||||||
|
void Debounce::loop() {
|
||||||
|
while (mRunning) {
|
||||||
|
mWaitSignal.wait();
|
||||||
|
if (!mRunning) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyIfReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Debounce::notifyIfReady() {
|
||||||
|
if (!mRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we haven't seen an event in more than the maximum wait time, notify callbacks immediately
|
||||||
|
// to ensure that we don't wait forever. Otherwise, wait for the minimum wait time and batch
|
||||||
|
// subsequent fast changes. This also means the first file change in a batch is notified immediately,
|
||||||
|
// separately from the rest of the batch. This seems like an acceptable tradeoff if the common case
|
||||||
|
// is that only a single file was updated at a time.
|
||||||
|
auto time = std::chrono::steady_clock::now();
|
||||||
|
if ((time - mLastTime) > std::chrono::milliseconds(MAX_WAIT_TIME)) {
|
||||||
|
mLastTime = time;
|
||||||
|
notify();
|
||||||
|
} else {
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debounce::wait() {
|
||||||
|
#ifdef __wasm32__
|
||||||
|
clear_timeout(mTimeout);
|
||||||
|
mTimeout = set_timeout(MIN_WAIT_TIME, this);
|
||||||
|
#else
|
||||||
|
auto status = mWaitSignal.waitFor(std::chrono::milliseconds(MIN_WAIT_TIME));
|
||||||
|
if (mRunning && (status == std::cv_status::timeout)) {
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debounce::notify() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
mLastTime = std::chrono::steady_clock::now();
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
auto cb = it->second;
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __wasm32__
|
||||||
|
mWaitSignal.reset();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
49
node_modules/@parcel/watcher/src/Debounce.hh
generated
vendored
Normal file
49
node_modules/@parcel/watcher/src/Debounce.hh
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef DEBOUNCE_H
|
||||||
|
#define DEBOUNCE_H
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
#include "Signal.hh"
|
||||||
|
|
||||||
|
#define MIN_WAIT_TIME 50
|
||||||
|
#define MAX_WAIT_TIME 500
|
||||||
|
|
||||||
|
#ifdef __wasm32__
|
||||||
|
extern "C" {
|
||||||
|
int set_timeout(int ms, void *ctx);
|
||||||
|
void clear_timeout(int timeout);
|
||||||
|
void on_timeout(void *ctx);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class Debounce {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<Debounce> getShared();
|
||||||
|
|
||||||
|
Debounce();
|
||||||
|
~Debounce();
|
||||||
|
|
||||||
|
void add(void *key, std::function<void()> cb);
|
||||||
|
void remove(void *key);
|
||||||
|
void trigger();
|
||||||
|
void notify();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mRunning;
|
||||||
|
std::mutex mMutex;
|
||||||
|
#ifdef __wasm32__
|
||||||
|
int mTimeout;
|
||||||
|
#else
|
||||||
|
Signal mWaitSignal;
|
||||||
|
std::thread mThread;
|
||||||
|
#endif
|
||||||
|
std::unordered_map<void *, std::function<void()>> mCallbacks;
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> mLastTime;
|
||||||
|
|
||||||
|
void loop();
|
||||||
|
void notifyIfReady();
|
||||||
|
void wait();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
164
node_modules/@parcel/watcher/src/DirTree.cc
generated
vendored
Normal file
164
node_modules/@parcel/watcher/src/DirTree.cc
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#include "DirTree.hh"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
// "Meyer's singleton", construction is ordered by use, likewise (reverse) for destruction.
|
||||||
|
// https://stackoverflow.com/a/17713799
|
||||||
|
// https://laristra.github.io/flecsi/src/developer-guide/patterns/meyers_singleton.html
|
||||||
|
static std::mutex& mDirCacheMutex() {
|
||||||
|
static std::mutex mutex;
|
||||||
|
return mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, std::weak_ptr<DirTree>>& dirTreeCache() {
|
||||||
|
static std::unordered_map<std::string, std::weak_ptr<DirTree>> cache;
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DirTreeDeleter {
|
||||||
|
void operator()(DirTree *tree) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
||||||
|
std::unordered_map<std::string, std::weak_ptr<DirTree>> &cache = dirTreeCache();
|
||||||
|
cache.erase(tree->root);
|
||||||
|
delete tree;
|
||||||
|
|
||||||
|
// Free up memory.
|
||||||
|
if (cache.size() == 0) {
|
||||||
|
cache.rehash(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<DirTree> DirTree::getCached(std::string root) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
||||||
|
std::unordered_map<std::string, std::weak_ptr<DirTree>> &cache = dirTreeCache();
|
||||||
|
|
||||||
|
auto found = cache.find(root);
|
||||||
|
std::shared_ptr<DirTree> tree;
|
||||||
|
|
||||||
|
// Use cached tree, or create an empty one.
|
||||||
|
if (found != cache.end()) {
|
||||||
|
tree = found->second.lock();
|
||||||
|
} else {
|
||||||
|
tree = std::shared_ptr<DirTree>(new DirTree(root), DirTreeDeleter());
|
||||||
|
cache.emplace(root, tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirTree::DirTree(std::string root, FILE *f) : root(root), isComplete(true) {
|
||||||
|
size_t size;
|
||||||
|
if (fscanf(f, "%zu", &size)) {
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
DirEntry entry(f);
|
||||||
|
entries.emplace(entry.path, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal find method that has no lock
|
||||||
|
DirEntry *DirTree::_find(std::string path) {
|
||||||
|
auto found = entries.find(path);
|
||||||
|
if (found == entries.end()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry *DirTree::add(std::string path, uint64_t mtime, bool isDir) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
||||||
|
|
||||||
|
DirEntry entry(path, mtime, isDir);
|
||||||
|
auto it = entries.emplace(entry.path, entry);
|
||||||
|
return &it.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry *DirTree::find(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
||||||
|
return _find(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry *DirTree::update(std::string path, uint64_t mtime) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
||||||
|
|
||||||
|
DirEntry *found = _find(path);
|
||||||
|
if (found) {
|
||||||
|
found->mtime = mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirTree::remove(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
||||||
|
|
||||||
|
DirEntry *found = _find(path);
|
||||||
|
|
||||||
|
// Remove all sub-entries if this is a directory
|
||||||
|
if (found && found->isDir) {
|
||||||
|
std::string pathStart = path + DIR_SEP;
|
||||||
|
for (auto it = entries.begin(); it != entries.end();) {
|
||||||
|
if (it->first.rfind(pathStart, 0) == 0) {
|
||||||
|
it = entries.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.erase(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirTree::write(FILE *f) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
||||||
|
|
||||||
|
fprintf(f, "%zu\n", entries.size());
|
||||||
|
for (auto it = entries.begin(); it != entries.end(); it++) {
|
||||||
|
it->second.write(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirTree::getChanges(DirTree *snapshot, EventList &events) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
||||||
|
std::lock_guard<std::mutex> snapshotLock(snapshot->mMutex);
|
||||||
|
|
||||||
|
for (auto it = entries.begin(); it != entries.end(); it++) {
|
||||||
|
auto found = snapshot->entries.find(it->first);
|
||||||
|
if (found == snapshot->entries.end()) {
|
||||||
|
events.create(it->second.path);
|
||||||
|
} else if (found->second.mtime != it->second.mtime && !found->second.isDir && !it->second.isDir) {
|
||||||
|
events.update(it->second.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = snapshot->entries.begin(); it != snapshot->entries.end(); it++) {
|
||||||
|
size_t count = entries.count(it->first);
|
||||||
|
if (count == 0) {
|
||||||
|
events.remove(it->second.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry::DirEntry(std::string p, uint64_t t, bool d) {
|
||||||
|
path = p;
|
||||||
|
mtime = t;
|
||||||
|
isDir = d;
|
||||||
|
state = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry::DirEntry(FILE *f) {
|
||||||
|
size_t size;
|
||||||
|
if (fscanf(f, "%zu", &size)) {
|
||||||
|
path.resize(size);
|
||||||
|
if (fread(&path[0], sizeof(char), size, f)) {
|
||||||
|
int d = 0;
|
||||||
|
fscanf(f, "%" PRIu64 " %d\n", &mtime, &d);
|
||||||
|
isDir = d == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirEntry::write(FILE *f) const {
|
||||||
|
fprintf(f, "%zu%s%" PRIu64 " %d\n", path.size(), path.c_str(), mtime, isDir);
|
||||||
|
}
|
||||||
50
node_modules/@parcel/watcher/src/DirTree.hh
generated
vendored
Normal file
50
node_modules/@parcel/watcher/src/DirTree.hh
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef DIR_TREE_H
|
||||||
|
#define DIR_TREE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
#include "Event.hh"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define DIR_SEP "\\"
|
||||||
|
#else
|
||||||
|
#define DIR_SEP "/"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct DirEntry {
|
||||||
|
std::string path;
|
||||||
|
uint64_t mtime;
|
||||||
|
bool isDir;
|
||||||
|
mutable void *state;
|
||||||
|
|
||||||
|
DirEntry(std::string p, uint64_t t, bool d);
|
||||||
|
DirEntry(FILE *f);
|
||||||
|
void write(FILE *f) const;
|
||||||
|
bool operator==(const DirEntry &other) const {
|
||||||
|
return path == other.path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DirTree {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<DirTree> getCached(std::string root);
|
||||||
|
DirTree(std::string root) : root(root), isComplete(false) {}
|
||||||
|
DirTree(std::string root, FILE *f);
|
||||||
|
DirEntry *add(std::string path, uint64_t mtime, bool isDir);
|
||||||
|
DirEntry *find(std::string path);
|
||||||
|
DirEntry *update(std::string path, uint64_t mtime);
|
||||||
|
void remove(std::string path);
|
||||||
|
void write(FILE *f);
|
||||||
|
void getChanges(DirTree *snapshot, EventList &events);
|
||||||
|
|
||||||
|
std::mutex mMutex;
|
||||||
|
std::string root;
|
||||||
|
bool isComplete;
|
||||||
|
std::unordered_map<std::string, DirEntry> entries;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DirEntry *_find(std::string path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
109
node_modules/@parcel/watcher/src/Event.hh
generated
vendored
Normal file
109
node_modules/@parcel/watcher/src/Event.hh
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#ifndef EVENT_H
|
||||||
|
#define EVENT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <node_api.h>
|
||||||
|
#include "wasm/include.h"
|
||||||
|
#include <napi.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
struct Event {
|
||||||
|
std::string path;
|
||||||
|
bool isCreated;
|
||||||
|
bool isDeleted;
|
||||||
|
Event(std::string path) : path(path), isCreated(false), isDeleted(false) {}
|
||||||
|
|
||||||
|
Value toJS(const Env& env) {
|
||||||
|
EscapableHandleScope scope(env);
|
||||||
|
Object res = Object::New(env);
|
||||||
|
std::string type = isCreated ? "create" : isDeleted ? "delete" : "update";
|
||||||
|
res.Set(String::New(env, "path"), String::New(env, path.c_str()));
|
||||||
|
res.Set(String::New(env, "type"), String::New(env, type.c_str()));
|
||||||
|
return scope.Escape(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EventList {
|
||||||
|
public:
|
||||||
|
void create(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
Event *event = internalUpdate(path);
|
||||||
|
if (event->isDeleted) {
|
||||||
|
// Assume update event when rapidly removed and created
|
||||||
|
// https://github.com/parcel-bundler/watcher/issues/72
|
||||||
|
event->isDeleted = false;
|
||||||
|
} else {
|
||||||
|
event->isCreated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event *update(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
return internalUpdate(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
Event *event = internalUpdate(path);
|
||||||
|
event->isDeleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
return mEvents.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Event> getEvents() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
std::vector<Event> eventsCloneVector;
|
||||||
|
for(auto it = mEvents.begin(); it != mEvents.end(); ++it) {
|
||||||
|
if (!(it->second.isCreated && it->second.isDeleted)) {
|
||||||
|
eventsCloneVector.push_back(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return eventsCloneVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
mEvents.clear();
|
||||||
|
mError.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void error(std::string err) {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
if (!mError.has_value()) {
|
||||||
|
mError.emplace(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasError() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
return mError.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getError() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
return mError.value_or("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex mMutex;
|
||||||
|
std::map<std::string, Event> mEvents;
|
||||||
|
std::optional<std::string> mError;
|
||||||
|
Event *internalUpdate(std::string path) {
|
||||||
|
auto found = mEvents.find(path);
|
||||||
|
if (found == mEvents.end()) {
|
||||||
|
auto it = mEvents.emplace(path, Event(path));
|
||||||
|
return &it.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &found->second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
22
node_modules/@parcel/watcher/src/Glob.cc
generated
vendored
Normal file
22
node_modules/@parcel/watcher/src/Glob.cc
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "Glob.hh"
|
||||||
|
|
||||||
|
#ifdef __wasm32__
|
||||||
|
extern "C" bool wasm_regex_match(const char *s, const char *regex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Glob::Glob(std::string raw) {
|
||||||
|
mRaw = raw;
|
||||||
|
mHash = std::hash<std::string>()(raw);
|
||||||
|
#ifndef __wasm32__
|
||||||
|
mRegex = std::regex(raw);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Glob::isIgnored(std::string relative_path) const {
|
||||||
|
// Use native JS regex engine for wasm to reduce binary size.
|
||||||
|
#ifdef __wasm32__
|
||||||
|
return wasm_regex_match(relative_path.c_str(), mRaw.c_str());
|
||||||
|
#else
|
||||||
|
return std::regex_match(relative_path, mRegex);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
34
node_modules/@parcel/watcher/src/Glob.hh
generated
vendored
Normal file
34
node_modules/@parcel/watcher/src/Glob.hh
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef GLOB_H
|
||||||
|
#define GLOB_H
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
struct Glob {
|
||||||
|
std::size_t mHash;
|
||||||
|
std::string mRaw;
|
||||||
|
#ifndef __wasm32__
|
||||||
|
std::regex mRegex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Glob(std::string raw);
|
||||||
|
|
||||||
|
bool operator==(const Glob &other) const {
|
||||||
|
return mHash == other.mHash && mRaw == other.mRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIgnored(std::string relative_path) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<Glob>
|
||||||
|
{
|
||||||
|
size_t operator()(const Glob& g) const {
|
||||||
|
return g.mHash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
101
node_modules/@parcel/watcher/src/PromiseRunner.hh
generated
vendored
Normal file
101
node_modules/@parcel/watcher/src/PromiseRunner.hh
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#ifndef PROMISE_RUNNER_H
|
||||||
|
#define PROMISE_RUNNER_H
|
||||||
|
|
||||||
|
#include <node_api.h>
|
||||||
|
#include "wasm/include.h"
|
||||||
|
#include <napi.h>
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
class PromiseRunner {
|
||||||
|
public:
|
||||||
|
const Env env;
|
||||||
|
Promise::Deferred deferred;
|
||||||
|
|
||||||
|
PromiseRunner(Env env) : env(env), deferred(Promise::Deferred::New(env)) {
|
||||||
|
napi_status status = napi_create_async_work(env, nullptr, env.Undefined(),
|
||||||
|
onExecute, onWorkComplete, this, &work);
|
||||||
|
if (status != napi_ok) {
|
||||||
|
work = nullptr;
|
||||||
|
const napi_extended_error_info *error_info = 0;
|
||||||
|
napi_get_last_error_info(env, &error_info);
|
||||||
|
if (error_info->error_message) {
|
||||||
|
Error::New(env, error_info->error_message).ThrowAsJavaScriptException();
|
||||||
|
} else {
|
||||||
|
Error::New(env).ThrowAsJavaScriptException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~PromiseRunner() {}
|
||||||
|
|
||||||
|
Value queue() {
|
||||||
|
if (work) {
|
||||||
|
napi_status status = napi_queue_async_work(env, work);
|
||||||
|
if (status != napi_ok) {
|
||||||
|
onError(Error::New(env));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.Promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
napi_async_work work;
|
||||||
|
std::string error;
|
||||||
|
|
||||||
|
static void onExecute(napi_env env, void *this_pointer) {
|
||||||
|
PromiseRunner* self = (PromiseRunner*) this_pointer;
|
||||||
|
try {
|
||||||
|
self->execute();
|
||||||
|
} catch (std::exception &err) {
|
||||||
|
self->error = err.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onWorkComplete(napi_env env, napi_status status, void *this_pointer) {
|
||||||
|
PromiseRunner* self = (PromiseRunner*) this_pointer;
|
||||||
|
if (status != napi_cancelled) {
|
||||||
|
HandleScope scope(self->env);
|
||||||
|
if (status == napi_ok) {
|
||||||
|
status = napi_delete_async_work(self->env, self->work);
|
||||||
|
if (status == napi_ok) {
|
||||||
|
if (self->error.size() == 0) {
|
||||||
|
self->onOK();
|
||||||
|
} else {
|
||||||
|
self->onError(Error::New(self->env, self->error));
|
||||||
|
}
|
||||||
|
delete self;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallthrough for error handling
|
||||||
|
const napi_extended_error_info *error_info = 0;
|
||||||
|
napi_get_last_error_info(env, &error_info);
|
||||||
|
if (error_info->error_message){
|
||||||
|
self->onError(Error::New(env, error_info->error_message));
|
||||||
|
} else {
|
||||||
|
self->onError(Error::New(env));
|
||||||
|
}
|
||||||
|
delete self;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void execute() {}
|
||||||
|
virtual Value getResult() {
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onOK() {
|
||||||
|
HandleScope scope(env);
|
||||||
|
Value result = getResult();
|
||||||
|
deferred.Resolve(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onError(const Error &e) {
|
||||||
|
deferred.Reject(e.Value());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
46
node_modules/@parcel/watcher/src/Signal.hh
generated
vendored
Normal file
46
node_modules/@parcel/watcher/src/Signal.hh
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#ifndef SIGNAL_H
|
||||||
|
#define SIGNAL_H
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
class Signal {
|
||||||
|
public:
|
||||||
|
Signal() : mFlag(false), mWaiting(false) {}
|
||||||
|
void wait() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
while (!mFlag) {
|
||||||
|
mWaiting = true;
|
||||||
|
mCond.wait(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cv_status waitFor(std::chrono::milliseconds ms) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
return mCond.wait_for(lock, ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notify() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mFlag = true;
|
||||||
|
mCond.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mFlag = false;
|
||||||
|
mWaiting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWaiting() {
|
||||||
|
return mWaiting;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mFlag;
|
||||||
|
bool mWaiting;
|
||||||
|
std::mutex mMutex;
|
||||||
|
std::condition_variable mCond;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
241
node_modules/@parcel/watcher/src/Watcher.cc
generated
vendored
Normal file
241
node_modules/@parcel/watcher/src/Watcher.cc
generated
vendored
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
#include "Watcher.hh"
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
struct WatcherHash {
|
||||||
|
std::size_t operator() (WatcherRef const &k) const {
|
||||||
|
return std::hash<std::string>()(k->mDir);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WatcherCompare {
|
||||||
|
size_t operator() (WatcherRef const &a, WatcherRef const &b) const {
|
||||||
|
return *a == *b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_set<WatcherRef , WatcherHash, WatcherCompare>& getSharedWatchers() {
|
||||||
|
static std::unordered_set<WatcherRef , WatcherHash, WatcherCompare>* sharedWatchers =
|
||||||
|
new std::unordered_set<WatcherRef , WatcherHash, WatcherCompare>();
|
||||||
|
return *sharedWatchers;
|
||||||
|
}
|
||||||
|
|
||||||
|
WatcherRef Watcher::getShared(std::string dir, std::unordered_set<std::string> ignorePaths, std::unordered_set<Glob> ignoreGlobs) {
|
||||||
|
WatcherRef watcher = std::make_shared<Watcher>(dir, ignorePaths, ignoreGlobs);
|
||||||
|
auto found = getSharedWatchers().find(watcher);
|
||||||
|
if (found != getSharedWatchers().end()) {
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSharedWatchers().insert(watcher);
|
||||||
|
return watcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeShared(Watcher *watcher) {
|
||||||
|
for (auto it = getSharedWatchers().begin(); it != getSharedWatchers().end(); it++) {
|
||||||
|
if (it->get() == watcher) {
|
||||||
|
getSharedWatchers().erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free up memory.
|
||||||
|
if (getSharedWatchers().size() == 0) {
|
||||||
|
getSharedWatchers().rehash(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Watcher::Watcher(std::string dir, std::unordered_set<std::string> ignorePaths, std::unordered_set<Glob> ignoreGlobs)
|
||||||
|
: mDir(dir),
|
||||||
|
mIgnorePaths(ignorePaths),
|
||||||
|
mIgnoreGlobs(ignoreGlobs) {
|
||||||
|
mDebounce = Debounce::getShared();
|
||||||
|
mDebounce->add(this, [this] () {
|
||||||
|
triggerCallbacks();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Watcher::~Watcher() {
|
||||||
|
mDebounce->remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::wait() {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
mCond.wait(lk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::notify() {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
mCond.notify_all();
|
||||||
|
|
||||||
|
if (mCallbacks.size() > 0 && mEvents.size() > 0) {
|
||||||
|
// We must release our lock before calling into the debouncer
|
||||||
|
// to avoid a deadlock: the debouncer thread itself will require
|
||||||
|
// our lock from its thread when calling into `triggerCallbacks`
|
||||||
|
// while holding its own debouncer lock.
|
||||||
|
lk.unlock();
|
||||||
|
mDebounce->trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CallbackData {
|
||||||
|
std::string error;
|
||||||
|
std::vector<Event> events;
|
||||||
|
CallbackData(std::string error, std::vector<Event> events) : error(error), events(events) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Value callbackEventsToJS(const Env &env, std::vector<Event> &events) {
|
||||||
|
EscapableHandleScope scope(env);
|
||||||
|
Array arr = Array::New(env, events.size());
|
||||||
|
uint32_t currentEventIndex = 0;
|
||||||
|
for (auto eventIterator = events.begin(); eventIterator != events.end(); eventIterator++) {
|
||||||
|
arr.Set(currentEventIndex++, eventIterator->toJS(env));
|
||||||
|
}
|
||||||
|
return scope.Escape(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callJSFunction(Napi::Env env, Function jsCallback, CallbackData *data) {
|
||||||
|
HandleScope scope(env);
|
||||||
|
auto err = data->error.size() > 0 ? Error::New(env, data->error).Value() : env.Null();
|
||||||
|
auto events = callbackEventsToJS(env, data->events);
|
||||||
|
jsCallback.Call({err, events});
|
||||||
|
delete data;
|
||||||
|
|
||||||
|
// Throw errors from the callback as fatal exceptions
|
||||||
|
// If we don't handle these node segfaults...
|
||||||
|
if (env.IsExceptionPending()) {
|
||||||
|
Napi::Error err = env.GetAndClearPendingException();
|
||||||
|
napi_fatal_exception(env, err.Value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::notifyError(std::exception &err) {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
CallbackData *data = new CallbackData(err.what(), {});
|
||||||
|
it->tsfn.BlockingCall(data, callJSFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called from the debounce thread.
|
||||||
|
void Watcher::triggerCallbacks() {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
if (mCallbacks.size() > 0 && (mEvents.size() > 0 || mEvents.hasError())) {
|
||||||
|
auto error = mEvents.getError();
|
||||||
|
auto events = mEvents.getEvents();
|
||||||
|
mEvents.clear();
|
||||||
|
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
it->tsfn.BlockingCall(new CallbackData(error, events), callJSFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be called from the JavaScript thread.
|
||||||
|
bool Watcher::watch(Function callback) {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
|
||||||
|
auto it = findCallback(callback);
|
||||||
|
if (it != mCallbacks.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tsfn = ThreadSafeFunction::New(
|
||||||
|
callback.Env(),
|
||||||
|
callback,
|
||||||
|
"Watcher callback",
|
||||||
|
0, // Unlimited queue
|
||||||
|
1 // Initial thread count
|
||||||
|
);
|
||||||
|
|
||||||
|
mCallbacks.push_back(Callback {
|
||||||
|
tsfn,
|
||||||
|
Napi::Persistent(callback),
|
||||||
|
std::this_thread::get_id()
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be called from the JavaScript thread.
|
||||||
|
std::vector<Callback>::iterator Watcher::findCallback(Function callback) {
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
// Only consider callbacks created by the same thread, or V8 will panic.
|
||||||
|
if (it->threadId == std::this_thread::get_id() && it->ref.Value() == callback) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mCallbacks.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be called from the JavaScript thread.
|
||||||
|
bool Watcher::unwatch(Function callback) {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
|
||||||
|
bool removed = false;
|
||||||
|
auto it = findCallback(callback);
|
||||||
|
if (it != mCallbacks.end()) {
|
||||||
|
it->tsfn.Release();
|
||||||
|
it->ref.Unref();
|
||||||
|
mCallbacks.erase(it);
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed && mCallbacks.size() == 0) {
|
||||||
|
unref();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::unref() {
|
||||||
|
if (mCallbacks.size() == 0) {
|
||||||
|
removeShared(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::destroy() {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
clearCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private because it doesn't lock.
|
||||||
|
void Watcher::clearCallbacks() {
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
it->tsfn.Release();
|
||||||
|
it->ref.Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallbacks.clear();
|
||||||
|
unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Watcher::isIgnored(std::string path) {
|
||||||
|
for (auto it = mIgnorePaths.begin(); it != mIgnorePaths.end(); it++) {
|
||||||
|
auto dir = *it + DIR_SEP;
|
||||||
|
if (*it == path || path.compare(0, dir.size(), dir) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto basePath = mDir + DIR_SEP;
|
||||||
|
|
||||||
|
if (path.rfind(basePath, 0) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto relativePath = path.substr(basePath.size());
|
||||||
|
|
||||||
|
for (auto it = mIgnoreGlobs.begin(); it != mIgnoreGlobs.end(); it++) {
|
||||||
|
if (it->isIgnored(relativePath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
73
node_modules/@parcel/watcher/src/Watcher.hh
generated
vendored
Normal file
73
node_modules/@parcel/watcher/src/Watcher.hh
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#ifndef WATCHER_H
|
||||||
|
#define WATCHER_H
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <set>
|
||||||
|
#include <node_api.h>
|
||||||
|
#include "Glob.hh"
|
||||||
|
#include "Event.hh"
|
||||||
|
#include "Debounce.hh"
|
||||||
|
#include "DirTree.hh"
|
||||||
|
#include "Signal.hh"
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
struct Watcher;
|
||||||
|
using WatcherRef = std::shared_ptr<Watcher>;
|
||||||
|
|
||||||
|
struct Callback {
|
||||||
|
Napi::ThreadSafeFunction tsfn;
|
||||||
|
Napi::FunctionReference ref;
|
||||||
|
std::thread::id threadId;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WatcherState {
|
||||||
|
public:
|
||||||
|
virtual ~WatcherState() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Watcher {
|
||||||
|
std::string mDir;
|
||||||
|
std::unordered_set<std::string> mIgnorePaths;
|
||||||
|
std::unordered_set<Glob> mIgnoreGlobs;
|
||||||
|
EventList mEvents;
|
||||||
|
std::shared_ptr<WatcherState> state;
|
||||||
|
|
||||||
|
Watcher(std::string dir, std::unordered_set<std::string> ignorePaths, std::unordered_set<Glob> ignoreGlobs);
|
||||||
|
~Watcher();
|
||||||
|
|
||||||
|
bool operator==(const Watcher &other) const {
|
||||||
|
return mDir == other.mDir && mIgnorePaths == other.mIgnorePaths && mIgnoreGlobs == other.mIgnoreGlobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait();
|
||||||
|
void notify();
|
||||||
|
void notifyError(std::exception &err);
|
||||||
|
bool watch(Function callback);
|
||||||
|
bool unwatch(Function callback);
|
||||||
|
void unref();
|
||||||
|
bool isIgnored(std::string path);
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
static WatcherRef getShared(std::string dir, std::unordered_set<std::string> ignorePaths, std::unordered_set<Glob> ignoreGlobs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mMutex;
|
||||||
|
std::condition_variable mCond;
|
||||||
|
std::vector<Callback> mCallbacks;
|
||||||
|
std::shared_ptr<Debounce> mDebounce;
|
||||||
|
|
||||||
|
std::vector<Callback>::iterator findCallback(Function callback);
|
||||||
|
void clearCallbacks();
|
||||||
|
void triggerCallbacks();
|
||||||
|
};
|
||||||
|
|
||||||
|
class WatcherError : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
WatcherRef mWatcher;
|
||||||
|
WatcherError(std::string msg, WatcherRef watcher) : std::runtime_error(msg), mWatcher(watcher) {}
|
||||||
|
WatcherError(const char *msg, WatcherRef watcher) : std::runtime_error(msg), mWatcher(watcher) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
268
node_modules/@parcel/watcher/src/binding.cc
generated
vendored
Normal file
268
node_modules/@parcel/watcher/src/binding.cc
generated
vendored
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
#include <unordered_set>
|
||||||
|
#include <node_api.h>
|
||||||
|
#include "wasm/include.h"
|
||||||
|
#include <napi.h>
|
||||||
|
#include "Glob.hh"
|
||||||
|
#include "Event.hh"
|
||||||
|
#include "Backend.hh"
|
||||||
|
#include "Watcher.hh"
|
||||||
|
#include "PromiseRunner.hh"
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
std::unordered_set<std::string> getIgnorePaths(Env env, Value opts) {
|
||||||
|
std::unordered_set<std::string> result;
|
||||||
|
|
||||||
|
if (opts.IsObject()) {
|
||||||
|
Value v = opts.As<Object>().Get(String::New(env, "ignorePaths"));
|
||||||
|
if (v.IsArray()) {
|
||||||
|
Array items = v.As<Array>();
|
||||||
|
for (size_t i = 0; i < items.Length(); i++) {
|
||||||
|
Value item = items.Get(Number::New(env, static_cast<double>(i)));
|
||||||
|
if (item.IsString()) {
|
||||||
|
result.insert(std::string(item.As<String>().Utf8Value().c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<Glob> getIgnoreGlobs(Env env, Value opts) {
|
||||||
|
std::unordered_set<Glob> result;
|
||||||
|
|
||||||
|
if (opts.IsObject()) {
|
||||||
|
Value v = opts.As<Object>().Get(String::New(env, "ignoreGlobs"));
|
||||||
|
if (v.IsArray()) {
|
||||||
|
Array items = v.As<Array>();
|
||||||
|
for (size_t i = 0; i < items.Length(); i++) {
|
||||||
|
Value item = items.Get(Number::New(env, static_cast<double>(i)));
|
||||||
|
if (item.IsString()) {
|
||||||
|
auto key = item.As<String>().Utf8Value();
|
||||||
|
try {
|
||||||
|
result.emplace(key);
|
||||||
|
} catch (const std::regex_error& e) {
|
||||||
|
Error::New(env, e.what()).ThrowAsJavaScriptException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Backend> getBackend(Env env, Value opts) {
|
||||||
|
Value b = opts.As<Object>().Get(String::New(env, "backend"));
|
||||||
|
std::string backendName;
|
||||||
|
if (b.IsString()) {
|
||||||
|
backendName = std::string(b.As<String>().Utf8Value().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Backend::getShared(backendName);
|
||||||
|
}
|
||||||
|
|
||||||
|
class WriteSnapshotRunner : public PromiseRunner {
|
||||||
|
public:
|
||||||
|
WriteSnapshotRunner(Env env, Value dir, Value snap, Value opts)
|
||||||
|
: PromiseRunner(env),
|
||||||
|
snapshotPath(std::string(snap.As<String>().Utf8Value().c_str())) {
|
||||||
|
watcher = Watcher::getShared(
|
||||||
|
std::string(dir.As<String>().Utf8Value().c_str()),
|
||||||
|
getIgnorePaths(env, opts),
|
||||||
|
getIgnoreGlobs(env, opts)
|
||||||
|
);
|
||||||
|
|
||||||
|
backend = getBackend(env, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
~WriteSnapshotRunner() {
|
||||||
|
watcher->unref();
|
||||||
|
backend->unref();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Backend> backend;
|
||||||
|
WatcherRef watcher;
|
||||||
|
std::string snapshotPath;
|
||||||
|
|
||||||
|
void execute() override {
|
||||||
|
backend->writeSnapshot(watcher, &snapshotPath);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GetEventsSinceRunner : public PromiseRunner {
|
||||||
|
public:
|
||||||
|
GetEventsSinceRunner(Env env, Value dir, Value snap, Value opts)
|
||||||
|
: PromiseRunner(env),
|
||||||
|
snapshotPath(std::string(snap.As<String>().Utf8Value().c_str())) {
|
||||||
|
watcher = std::make_shared<Watcher>(
|
||||||
|
std::string(dir.As<String>().Utf8Value().c_str()),
|
||||||
|
getIgnorePaths(env, opts),
|
||||||
|
getIgnoreGlobs(env, opts)
|
||||||
|
);
|
||||||
|
|
||||||
|
backend = getBackend(env, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
~GetEventsSinceRunner() {
|
||||||
|
watcher->unref();
|
||||||
|
backend->unref();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Backend> backend;
|
||||||
|
WatcherRef watcher;
|
||||||
|
std::string snapshotPath;
|
||||||
|
|
||||||
|
void execute() override {
|
||||||
|
backend->getEventsSince(watcher, &snapshotPath);
|
||||||
|
if (watcher->mEvents.hasError()) {
|
||||||
|
throw std::runtime_error(watcher->mEvents.getError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value getResult() override {
|
||||||
|
std::vector<Event> events = watcher->mEvents.getEvents();
|
||||||
|
Array eventsArray = Array::New(env, events.size());
|
||||||
|
uint32_t i = 0;
|
||||||
|
for (auto it = events.begin(); it != events.end(); it++) {
|
||||||
|
eventsArray.Set(i++, it->toJS(env));
|
||||||
|
}
|
||||||
|
return eventsArray;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Runner>
|
||||||
|
Value queueSnapshotWork(const CallbackInfo& info) {
|
||||||
|
Env env = info.Env();
|
||||||
|
if (info.Length() < 1 || !info[0].IsString()) {
|
||||||
|
TypeError::New(env, "Expected a string").ThrowAsJavaScriptException();
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.Length() < 2 || !info[1].IsString()) {
|
||||||
|
TypeError::New(env, "Expected a string").ThrowAsJavaScriptException();
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.Length() >= 3 && !info[2].IsObject()) {
|
||||||
|
TypeError::New(env, "Expected an object").ThrowAsJavaScriptException();
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Runner *runner = new Runner(info.Env(), info[0], info[1], info[2]);
|
||||||
|
return runner->queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value writeSnapshot(const CallbackInfo& info) {
|
||||||
|
return queueSnapshotWork<WriteSnapshotRunner>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value getEventsSince(const CallbackInfo& info) {
|
||||||
|
return queueSnapshotWork<GetEventsSinceRunner>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubscribeRunner : public PromiseRunner {
|
||||||
|
public:
|
||||||
|
SubscribeRunner(Env env, Value dir, Value fn, Value opts) : PromiseRunner(env) {
|
||||||
|
watcher = Watcher::getShared(
|
||||||
|
std::string(dir.As<String>().Utf8Value().c_str()),
|
||||||
|
getIgnorePaths(env, opts),
|
||||||
|
getIgnoreGlobs(env, opts)
|
||||||
|
);
|
||||||
|
|
||||||
|
backend = getBackend(env, opts);
|
||||||
|
watcher->watch(fn.As<Function>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WatcherRef watcher;
|
||||||
|
std::shared_ptr<Backend> backend;
|
||||||
|
FunctionReference callback;
|
||||||
|
|
||||||
|
void execute() override {
|
||||||
|
try {
|
||||||
|
backend->watch(watcher);
|
||||||
|
} catch (std::exception&) {
|
||||||
|
watcher->destroy();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnsubscribeRunner : public PromiseRunner {
|
||||||
|
public:
|
||||||
|
UnsubscribeRunner(Env env, Value dir, Value fn, Value opts) : PromiseRunner(env) {
|
||||||
|
watcher = Watcher::getShared(
|
||||||
|
std::string(dir.As<String>().Utf8Value().c_str()),
|
||||||
|
getIgnorePaths(env, opts),
|
||||||
|
getIgnoreGlobs(env, opts)
|
||||||
|
);
|
||||||
|
|
||||||
|
backend = getBackend(env, opts);
|
||||||
|
shouldUnwatch = watcher->unwatch(fn.As<Function>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WatcherRef watcher;
|
||||||
|
std::shared_ptr<Backend> backend;
|
||||||
|
bool shouldUnwatch;
|
||||||
|
|
||||||
|
void execute() override {
|
||||||
|
if (shouldUnwatch) {
|
||||||
|
backend->unwatch(watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Runner>
|
||||||
|
Value queueSubscriptionWork(const CallbackInfo& info) {
|
||||||
|
Env env = info.Env();
|
||||||
|
if (info.Length() < 1 || !info[0].IsString()) {
|
||||||
|
TypeError::New(env, "Expected a string").ThrowAsJavaScriptException();
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.Length() < 2 || !info[1].IsFunction()) {
|
||||||
|
TypeError::New(env, "Expected a function").ThrowAsJavaScriptException();
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.Length() >= 3 && !info[2].IsObject()) {
|
||||||
|
TypeError::New(env, "Expected an object").ThrowAsJavaScriptException();
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Runner *runner = new Runner(info.Env(), info[0], info[1], info[2]);
|
||||||
|
return runner->queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value subscribe(const CallbackInfo& info) {
|
||||||
|
return queueSubscriptionWork<SubscribeRunner>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value unsubscribe(const CallbackInfo& info) {
|
||||||
|
return queueSubscriptionWork<UnsubscribeRunner>(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object Init(Env env, Object exports) {
|
||||||
|
exports.Set(
|
||||||
|
String::New(env, "writeSnapshot"),
|
||||||
|
Function::New(env, writeSnapshot)
|
||||||
|
);
|
||||||
|
exports.Set(
|
||||||
|
String::New(env, "getEventsSince"),
|
||||||
|
Function::New(env, getEventsSince)
|
||||||
|
);
|
||||||
|
exports.Set(
|
||||||
|
String::New(env, "subscribe"),
|
||||||
|
Function::New(env, subscribe)
|
||||||
|
);
|
||||||
|
exports.Set(
|
||||||
|
String::New(env, "unsubscribe"),
|
||||||
|
Function::New(env, unsubscribe)
|
||||||
|
);
|
||||||
|
return exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_API_MODULE(watcher, Init)
|
||||||
306
node_modules/@parcel/watcher/src/kqueue/KqueueBackend.cc
generated
vendored
Normal file
306
node_modules/@parcel/watcher/src/kqueue/KqueueBackend.cc
generated
vendored
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "KqueueBackend.hh"
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
|
#define st_mtim st_mtimespec
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(O_EVTONLY)
|
||||||
|
#define O_EVTONLY O_RDONLY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CONVERT_TIME(ts) ((uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec)
|
||||||
|
|
||||||
|
void KqueueBackend::start() {
|
||||||
|
if ((mKqueue = kqueue()) < 0) {
|
||||||
|
throw std::runtime_error(std::string("Unable to open kqueue: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a pipe that we will write to when we want to end the thread.
|
||||||
|
int err = pipe(mPipe);
|
||||||
|
if (err == -1) {
|
||||||
|
throw std::runtime_error(std::string("Unable to open pipe: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe kqueue to this pipe.
|
||||||
|
struct kevent ev;
|
||||||
|
EV_SET(
|
||||||
|
&ev,
|
||||||
|
mPipe[0],
|
||||||
|
EVFILT_READ,
|
||||||
|
EV_ADD | EV_CLEAR,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (kevent(mKqueue, &ev, 1, NULL, 0, 0)) {
|
||||||
|
close(mPipe[0]);
|
||||||
|
close(mPipe[1]);
|
||||||
|
throw std::runtime_error(std::string("Unable to watch pipe: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyStarted();
|
||||||
|
|
||||||
|
struct kevent events[128];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int event_count = kevent(mKqueue, NULL, 0, events, 128, 0);
|
||||||
|
if (event_count < 0 || events[0].flags == EV_ERROR) {
|
||||||
|
throw std::runtime_error(std::string("kevent error: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track all of the watchers that are touched so we can notify them at the end of the events.
|
||||||
|
std::unordered_set<WatcherRef> watchers;
|
||||||
|
|
||||||
|
for (int i = 0; i < event_count; i++) {
|
||||||
|
int flags = events[i].fflags;
|
||||||
|
int fd = events[i].ident;
|
||||||
|
if (fd == mPipe[0]) {
|
||||||
|
// pipe was written to. break out of the loop.
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = mFdToEntry.find(fd);
|
||||||
|
if (it == mFdToEntry.end()) {
|
||||||
|
// If fd wasn't in our map, we may have already stopped watching it. Ignore the event.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry *entry = it->second;
|
||||||
|
|
||||||
|
if (flags & NOTE_WRITE && entry && entry->isDir) {
|
||||||
|
// If a write occurred on a directory, we have to diff the contents of that
|
||||||
|
// directory to determine what file was added/deleted.
|
||||||
|
compareDir(fd, entry->path, watchers);
|
||||||
|
} else {
|
||||||
|
std::vector<KqueueSubscription *> subs = findSubscriptions(entry->path);
|
||||||
|
for (auto it = subs.begin(); it != subs.end(); it++) {
|
||||||
|
KqueueSubscription *sub = *it;
|
||||||
|
watchers.insert(sub->watcher);
|
||||||
|
if (flags & (NOTE_DELETE | NOTE_RENAME | NOTE_REVOKE)) {
|
||||||
|
sub->watcher->mEvents.remove(sub->path);
|
||||||
|
sub->tree->remove(sub->path);
|
||||||
|
mFdToEntry.erase((int)(size_t)entry->state);
|
||||||
|
mSubscriptions.erase(sub->path);
|
||||||
|
} else if (flags & (NOTE_WRITE | NOTE_ATTRIB | NOTE_EXTEND)) {
|
||||||
|
struct stat st;
|
||||||
|
lstat(sub->path.c_str(), &st);
|
||||||
|
if (entry->mtime != CONVERT_TIME(st.st_mtim)) {
|
||||||
|
entry->mtime = CONVERT_TIME(st.st_mtim);
|
||||||
|
sub->watcher->mEvents.update(sub->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = watchers.begin(); it != watchers.end(); it++) {
|
||||||
|
(*it)->notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
close(mPipe[0]);
|
||||||
|
close(mPipe[1]);
|
||||||
|
mEndedSignal.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
KqueueBackend::~KqueueBackend() {
|
||||||
|
write(mPipe[1], "X", 1);
|
||||||
|
mEndedSignal.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KqueueBackend::subscribe(WatcherRef watcher) {
|
||||||
|
// Build a full directory tree recursively, and watch each directory.
|
||||||
|
std::shared_ptr<DirTree> tree = getTree(watcher);
|
||||||
|
|
||||||
|
for (auto it = tree->entries.begin(); it != tree->entries.end(); it++) {
|
||||||
|
bool success = watchDir(watcher, it->second.path, tree);
|
||||||
|
if (!success) {
|
||||||
|
throw WatcherError(std::string("error watching " + watcher->mDir + ": " + strerror(errno)), watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KqueueBackend::watchDir(WatcherRef watcher, std::string path, std::shared_ptr<DirTree> tree) {
|
||||||
|
if (watcher->isIgnored(path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry *entry = tree->find(path);
|
||||||
|
if (!entry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
KqueueSubscription sub = {
|
||||||
|
.watcher = watcher,
|
||||||
|
.path = path,
|
||||||
|
.tree = tree
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!entry->state) {
|
||||||
|
int fd = open(path.c_str(), O_EVTONLY);
|
||||||
|
if (fd <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kevent event;
|
||||||
|
EV_SET(
|
||||||
|
&event,
|
||||||
|
fd,
|
||||||
|
EVFILT_VNODE,
|
||||||
|
EV_ADD | EV_CLEAR | EV_ENABLE,
|
||||||
|
NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (kevent(mKqueue, &event, 1, NULL, 0, 0)) {
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->state = (void *)(size_t)fd;
|
||||||
|
mFdToEntry.emplace(fd, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub.fd = (int)(size_t)entry->state;
|
||||||
|
mSubscriptions.emplace(path, sub);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<KqueueSubscription *> KqueueBackend::findSubscriptions(std::string &path) {
|
||||||
|
// Find the subscriptions affected by this path.
|
||||||
|
// Copy pointers to them into a vector so that modifying mSubscriptions doesn't invalidate the iterator.
|
||||||
|
auto range = mSubscriptions.equal_range(path);
|
||||||
|
std::vector<KqueueSubscription *> subs;
|
||||||
|
for (auto it = range.first; it != range.second; it++) {
|
||||||
|
subs.push_back(&it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KqueueBackend::compareDir(int fd, std::string &path, std::unordered_set<WatcherRef> &watchers) {
|
||||||
|
// macOS doesn't support fdclosedir, so we have to duplicate the file descriptor
|
||||||
|
// to ensure the closedir doesn't also stop watching.
|
||||||
|
#if __APPLE__
|
||||||
|
fd = dup(fd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DIR *dir = fdopendir(fd);
|
||||||
|
if (dir == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fdopendir doesn't rewind to the beginning.
|
||||||
|
rewinddir(dir);
|
||||||
|
|
||||||
|
std::vector<KqueueSubscription *> subs = findSubscriptions(path);
|
||||||
|
std::string dirStart = path + DIR_SEP;
|
||||||
|
|
||||||
|
std::unordered_set<std::shared_ptr<DirTree>> trees;
|
||||||
|
for (auto it = subs.begin(); it != subs.end(); it++) {
|
||||||
|
trees.emplace((*it)->tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<std::string> entries;
|
||||||
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir(dir))) {
|
||||||
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fullpath = dirStart + entry->d_name;
|
||||||
|
entries.emplace(fullpath);
|
||||||
|
|
||||||
|
for (auto it = trees.begin(); it != trees.end(); it++) {
|
||||||
|
std::shared_ptr<DirTree> tree = *it;
|
||||||
|
if (!tree->find(fullpath)) {
|
||||||
|
struct stat st;
|
||||||
|
fstatat(fd, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
|
||||||
|
tree->add(fullpath, CONVERT_TIME(st.st_mtim), S_ISDIR(st.st_mode));
|
||||||
|
|
||||||
|
// Notify all watchers with the same tree.
|
||||||
|
for (auto i = subs.begin(); i != subs.end(); i++) {
|
||||||
|
KqueueSubscription *sub = *i;
|
||||||
|
if (sub->tree == tree) {
|
||||||
|
if (sub->watcher->isIgnored(fullpath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub->watcher->mEvents.create(fullpath);
|
||||||
|
watchers.emplace(sub->watcher);
|
||||||
|
|
||||||
|
bool success = watchDir(sub->watcher, fullpath, sub->tree);
|
||||||
|
if (!success) {
|
||||||
|
sub->tree->remove(fullpath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = trees.begin(); it != trees.end(); it++) {
|
||||||
|
std::shared_ptr<DirTree> tree = *it;
|
||||||
|
for (auto entry = tree->entries.begin(); entry != tree->entries.end();) {
|
||||||
|
|
||||||
|
if (
|
||||||
|
entry->first.rfind(dirStart, 0) == 0 &&
|
||||||
|
entry->first.find(DIR_SEP, dirStart.length()) == std::string::npos &&
|
||||||
|
entries.count(entry->first) == 0
|
||||||
|
) {
|
||||||
|
// Notify all watchers with the same tree.
|
||||||
|
for (auto i = subs.begin(); i != subs.end(); i++) {
|
||||||
|
if ((*i)->tree == tree) {
|
||||||
|
KqueueSubscription *sub = *i;
|
||||||
|
if (!sub->watcher->isIgnored(entry->first)) {
|
||||||
|
sub->watcher->mEvents.remove(entry->first);
|
||||||
|
watchers.emplace(sub->watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mFdToEntry.erase((int)(size_t)entry->second.state);
|
||||||
|
mSubscriptions.erase(entry->first);
|
||||||
|
entry = tree->entries.erase(entry);
|
||||||
|
} else {
|
||||||
|
entry++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
|
closedir(dir);
|
||||||
|
#else
|
||||||
|
fdclosedir(dir);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KqueueBackend::unsubscribe(WatcherRef watcher) {
|
||||||
|
// Find any subscriptions pointing to this watcher, and remove them.
|
||||||
|
for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
|
||||||
|
if (it->second.watcher.get() == watcher.get()) {
|
||||||
|
if (mSubscriptions.count(it->first) == 1) {
|
||||||
|
// Closing the file descriptor automatically unwatches it in the kqueue.
|
||||||
|
close(it->second.fd);
|
||||||
|
mFdToEntry.erase(it->second.fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
it = mSubscriptions.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
node_modules/@parcel/watcher/src/kqueue/KqueueBackend.hh
generated
vendored
Normal file
35
node_modules/@parcel/watcher/src/kqueue/KqueueBackend.hh
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef KQUEUE_H
|
||||||
|
#define KQUEUE_H
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <sys/event.h>
|
||||||
|
#include "../shared/BruteForceBackend.hh"
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
#include "../Signal.hh"
|
||||||
|
|
||||||
|
struct KqueueSubscription {
|
||||||
|
WatcherRef watcher;
|
||||||
|
std::string path;
|
||||||
|
std::shared_ptr<DirTree> tree;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KqueueBackend : public BruteForceBackend {
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
~KqueueBackend();
|
||||||
|
void subscribe(WatcherRef watcher) override;
|
||||||
|
void unsubscribe(WatcherRef watcher) override;
|
||||||
|
private:
|
||||||
|
int mKqueue;
|
||||||
|
int mPipe[2];
|
||||||
|
std::unordered_multimap<std::string, KqueueSubscription> mSubscriptions;
|
||||||
|
std::unordered_map<int, DirEntry *> mFdToEntry;
|
||||||
|
Signal mEndedSignal;
|
||||||
|
|
||||||
|
bool watchDir(WatcherRef watcher, std::string path, std::shared_ptr<DirTree> tree);
|
||||||
|
bool compareDir(int fd, std::string &dir, std::unordered_set<WatcherRef> &watchers);
|
||||||
|
std::vector<KqueueSubscription *> findSubscriptions(std::string &path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
236
node_modules/@parcel/watcher/src/linux/InotifyBackend.cc
generated
vendored
Normal file
236
node_modules/@parcel/watcher/src/linux/InotifyBackend.cc
generated
vendored
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "InotifyBackend.hh"
|
||||||
|
|
||||||
|
#define INOTIFY_MASK \
|
||||||
|
IN_ATTRIB | IN_CREATE | IN_DELETE | \
|
||||||
|
IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_MOVED_FROM | \
|
||||||
|
IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR | IN_EXCL_UNLINK
|
||||||
|
#define BUFFER_SIZE 8192
|
||||||
|
#define CONVERT_TIME(ts) ((uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec)
|
||||||
|
|
||||||
|
void InotifyBackend::start() {
|
||||||
|
// Create a pipe that we will write to when we want to end the thread.
|
||||||
|
int err = pipe2(mPipe, O_CLOEXEC | O_NONBLOCK);
|
||||||
|
if (err == -1) {
|
||||||
|
throw std::runtime_error(std::string("Unable to open pipe: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init inotify file descriptor.
|
||||||
|
mInotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||||
|
if (mInotify == -1) {
|
||||||
|
throw std::runtime_error(std::string("Unable to initialize inotify: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
pollfd pollfds[2];
|
||||||
|
pollfds[0].fd = mPipe[0];
|
||||||
|
pollfds[0].events = POLLIN;
|
||||||
|
pollfds[0].revents = 0;
|
||||||
|
pollfds[1].fd = mInotify;
|
||||||
|
pollfds[1].events = POLLIN;
|
||||||
|
pollfds[1].revents = 0;
|
||||||
|
|
||||||
|
notifyStarted();
|
||||||
|
|
||||||
|
// Loop until we get an event from the pipe.
|
||||||
|
while (true) {
|
||||||
|
int result = poll(pollfds, 2, 500);
|
||||||
|
if (result < 0) {
|
||||||
|
throw std::runtime_error(std::string("Unable to poll: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pollfds[0].revents) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pollfds[1].revents) {
|
||||||
|
handleEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(mPipe[0]);
|
||||||
|
close(mPipe[1]);
|
||||||
|
close(mInotify);
|
||||||
|
|
||||||
|
mEndedSignal.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
InotifyBackend::~InotifyBackend() {
|
||||||
|
write(mPipe[1], "X", 1);
|
||||||
|
mEndedSignal.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called by Backend::watch which takes a lock on mMutex
|
||||||
|
void InotifyBackend::subscribe(WatcherRef watcher) {
|
||||||
|
// Build a full directory tree recursively, and watch each directory.
|
||||||
|
std::shared_ptr<DirTree> tree = getTree(watcher);
|
||||||
|
|
||||||
|
for (auto it = tree->entries.begin(); it != tree->entries.end(); it++) {
|
||||||
|
if (it->second.isDir) {
|
||||||
|
bool success = watchDir(watcher, it->second.path, tree);
|
||||||
|
if (!success) {
|
||||||
|
throw WatcherError(std::string("inotify_add_watch on '") + it->second.path + std::string("' failed: ") + strerror(errno), watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InotifyBackend::watchDir(WatcherRef watcher, std::string path, std::shared_ptr<DirTree> tree) {
|
||||||
|
int wd = inotify_add_watch(mInotify, path.c_str(), INOTIFY_MASK);
|
||||||
|
if (wd == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<InotifySubscription> sub = std::make_shared<InotifySubscription>();
|
||||||
|
sub->tree = tree;
|
||||||
|
sub->path = path;
|
||||||
|
sub->watcher = watcher;
|
||||||
|
mSubscriptions.emplace(wd, sub);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InotifyBackend::handleEvents() {
|
||||||
|
char buf[BUFFER_SIZE] __attribute__ ((aligned(__alignof__(struct inotify_event))));;
|
||||||
|
struct inotify_event *event;
|
||||||
|
|
||||||
|
// Track all of the watchers that are touched so we can notify them at the end of the events.
|
||||||
|
std::unordered_set<WatcherRef> watchers;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int n = read(mInotify, &buf, BUFFER_SIZE);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error(std::string("Error reading from inotify: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (char *ptr = buf; ptr < buf + n; ptr += sizeof(*event) + event->len) {
|
||||||
|
event = (struct inotify_event *)ptr;
|
||||||
|
|
||||||
|
if ((event->mask & IN_Q_OVERFLOW) == IN_Q_OVERFLOW) {
|
||||||
|
// overflow
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(event, watchers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = watchers.begin(); it != watchers.end(); it++) {
|
||||||
|
(*it)->notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InotifyBackend::handleEvent(struct inotify_event *event, std::unordered_set<WatcherRef> &watchers) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
// Find the subscriptions for this watch descriptor
|
||||||
|
auto range = mSubscriptions.equal_range(event->wd);
|
||||||
|
std::unordered_set<std::shared_ptr<InotifySubscription>> set;
|
||||||
|
for (auto it = range.first; it != range.second; it++) {
|
||||||
|
set.insert(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = set.begin(); it != set.end(); it++) {
|
||||||
|
if (handleSubscription(event, *it)) {
|
||||||
|
watchers.insert((*it)->watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InotifyBackend::handleSubscription(struct inotify_event *event, std::shared_ptr<InotifySubscription> sub) {
|
||||||
|
// Build full path and check if its in our ignore list.
|
||||||
|
std::shared_ptr<Watcher> watcher = sub->watcher;
|
||||||
|
std::string path = std::string(sub->path);
|
||||||
|
bool isDir = event->mask & IN_ISDIR;
|
||||||
|
|
||||||
|
if (event->len > 0) {
|
||||||
|
path += "/" + std::string(event->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watcher->isIgnored(path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a create, check if it's a directory and start watching if it is.
|
||||||
|
// In any case, keep the directory tree up to date.
|
||||||
|
if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
|
||||||
|
watcher->mEvents.create(path);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
// Use lstat to avoid resolving symbolic links that we cannot watch anyway
|
||||||
|
// https://github.com/parcel-bundler/watcher/issues/76
|
||||||
|
if (lstat(path.c_str(), &st) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DirEntry *entry = sub->tree->add(path, CONVERT_TIME(st.st_mtim), S_ISDIR(st.st_mode));
|
||||||
|
|
||||||
|
if (entry->isDir) {
|
||||||
|
bool success = watchDir(watcher, path, sub->tree);
|
||||||
|
if (!success) {
|
||||||
|
sub->tree->remove(path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event->mask & (IN_MODIFY | IN_ATTRIB)) {
|
||||||
|
watcher->mEvents.update(path);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(path.c_str(), &st) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sub->tree->update(path, CONVERT_TIME(st.st_mtim));
|
||||||
|
} else if (event->mask & (IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVE_SELF)) {
|
||||||
|
bool isSelfEvent = (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF));
|
||||||
|
// Ignore delete/move self events unless this is the recursive watch root
|
||||||
|
if (isSelfEvent && path != watcher->mDir) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entry being deleted/moved is a directory, remove it from the list of subscriptions
|
||||||
|
// XXX: self events don't have the IN_ISDIR mask
|
||||||
|
if (isSelfEvent || isDir) {
|
||||||
|
for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
|
||||||
|
if (it->second->path == path) {
|
||||||
|
it = mSubscriptions.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher->mEvents.remove(path);
|
||||||
|
sub->tree->remove(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called by Backend::unwatch which takes a lock on mMutex
|
||||||
|
void InotifyBackend::unsubscribe(WatcherRef watcher) {
|
||||||
|
// Find any subscriptions pointing to this watcher, and remove them.
|
||||||
|
for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
|
||||||
|
if (it->second->watcher.get() == watcher.get()) {
|
||||||
|
if (mSubscriptions.count(it->first) == 1) {
|
||||||
|
int err = inotify_rm_watch(mInotify, it->first);
|
||||||
|
if (err == -1) {
|
||||||
|
throw WatcherError(std::string("Unable to remove watcher: ") + strerror(errno), watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it = mSubscriptions.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
node_modules/@parcel/watcher/src/linux/InotifyBackend.hh
generated
vendored
Normal file
34
node_modules/@parcel/watcher/src/linux/InotifyBackend.hh
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef INOTIFY_H
|
||||||
|
#define INOTIFY_H
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include "../shared/BruteForceBackend.hh"
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
#include "../Signal.hh"
|
||||||
|
|
||||||
|
struct InotifySubscription {
|
||||||
|
std::shared_ptr<DirTree> tree;
|
||||||
|
std::string path;
|
||||||
|
WatcherRef watcher;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InotifyBackend : public BruteForceBackend {
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
~InotifyBackend();
|
||||||
|
void subscribe(WatcherRef watcher) override;
|
||||||
|
void unsubscribe(WatcherRef watcher) override;
|
||||||
|
private:
|
||||||
|
int mPipe[2];
|
||||||
|
int mInotify;
|
||||||
|
std::unordered_multimap<int, std::shared_ptr<InotifySubscription>> mSubscriptions;
|
||||||
|
Signal mEndedSignal;
|
||||||
|
|
||||||
|
bool watchDir(WatcherRef watcher, std::string path, std::shared_ptr<DirTree> tree);
|
||||||
|
void handleEvents();
|
||||||
|
void handleEvent(struct inotify_event *event, std::unordered_set<WatcherRef> &watchers);
|
||||||
|
bool handleSubscription(struct inotify_event *event, std::shared_ptr<InotifySubscription> sub);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
338
node_modules/@parcel/watcher/src/macos/FSEventsBackend.cc
generated
vendored
Normal file
338
node_modules/@parcel/watcher/src/macos/FSEventsBackend.cc
generated
vendored
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include "../Event.hh"
|
||||||
|
#include "../Backend.hh"
|
||||||
|
#include "./FSEventsBackend.hh"
|
||||||
|
#include "../Watcher.hh"
|
||||||
|
|
||||||
|
#define CONVERT_TIME(ts) ((uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec)
|
||||||
|
#define IGNORED_FLAGS (kFSEventStreamEventFlagItemIsHardlink | kFSEventStreamEventFlagItemIsLastHardlink | kFSEventStreamEventFlagItemIsSymlink | kFSEventStreamEventFlagItemIsDir | kFSEventStreamEventFlagItemIsFile)
|
||||||
|
|
||||||
|
void stopStream(FSEventStreamRef stream, CFRunLoopRef runLoop) {
|
||||||
|
FSEventStreamStop(stream);
|
||||||
|
FSEventStreamUnscheduleFromRunLoop(stream, runLoop, kCFRunLoopDefaultMode);
|
||||||
|
FSEventStreamInvalidate(stream);
|
||||||
|
FSEventStreamRelease(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// macOS has a case insensitive file system by default. In order to detect
|
||||||
|
// file renames that only affect case, we need to get the canonical path
|
||||||
|
// and compare it with the input path to determine if a file was created or deleted.
|
||||||
|
bool pathExists(char *path) {
|
||||||
|
int fd = open(path, O_RDONLY | O_SYMLINK);
|
||||||
|
if (fd == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
if (fcntl(fd, F_GETPATH, buf) == -1) {
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool res = strncmp(path, buf, PATH_MAX) == 0;
|
||||||
|
close(fd);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
class State: public WatcherState {
|
||||||
|
public:
|
||||||
|
FSEventStreamRef stream;
|
||||||
|
std::shared_ptr<DirTree> tree;
|
||||||
|
uint64_t since;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FSEventsCallback(
|
||||||
|
ConstFSEventStreamRef streamRef,
|
||||||
|
void *clientCallBackInfo,
|
||||||
|
size_t numEvents,
|
||||||
|
void *eventPaths,
|
||||||
|
const FSEventStreamEventFlags eventFlags[],
|
||||||
|
const FSEventStreamEventId eventIds[]
|
||||||
|
) {
|
||||||
|
char **paths = (char **)eventPaths;
|
||||||
|
std::shared_ptr<Watcher>& watcher = *static_cast<std::shared_ptr<Watcher> *>(clientCallBackInfo);
|
||||||
|
|
||||||
|
EventList& list = watcher->mEvents;
|
||||||
|
if (watcher->state == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto stateGuard = watcher->state;
|
||||||
|
auto* state = static_cast<State*>(stateGuard.get());
|
||||||
|
uint64_t since = state->since;
|
||||||
|
bool deletedRoot = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numEvents; ++i) {
|
||||||
|
bool isCreated = (eventFlags[i] & kFSEventStreamEventFlagItemCreated) == kFSEventStreamEventFlagItemCreated;
|
||||||
|
bool isRemoved = (eventFlags[i] & kFSEventStreamEventFlagItemRemoved) == kFSEventStreamEventFlagItemRemoved;
|
||||||
|
bool isModified = (eventFlags[i] & kFSEventStreamEventFlagItemModified) == kFSEventStreamEventFlagItemModified ||
|
||||||
|
(eventFlags[i] & kFSEventStreamEventFlagItemInodeMetaMod) == kFSEventStreamEventFlagItemInodeMetaMod ||
|
||||||
|
(eventFlags[i] & kFSEventStreamEventFlagItemFinderInfoMod) == kFSEventStreamEventFlagItemFinderInfoMod ||
|
||||||
|
(eventFlags[i] & kFSEventStreamEventFlagItemChangeOwner) == kFSEventStreamEventFlagItemChangeOwner ||
|
||||||
|
(eventFlags[i] & kFSEventStreamEventFlagItemXattrMod) == kFSEventStreamEventFlagItemXattrMod;
|
||||||
|
bool isRenamed = (eventFlags[i] & kFSEventStreamEventFlagItemRenamed) == kFSEventStreamEventFlagItemRenamed;
|
||||||
|
bool isDone = (eventFlags[i] & kFSEventStreamEventFlagHistoryDone) == kFSEventStreamEventFlagHistoryDone;
|
||||||
|
bool isDir = (eventFlags[i] & kFSEventStreamEventFlagItemIsDir) == kFSEventStreamEventFlagItemIsDir;
|
||||||
|
|
||||||
|
|
||||||
|
if (eventFlags[i] & kFSEventStreamEventFlagMustScanSubDirs) {
|
||||||
|
if (eventFlags[i] & kFSEventStreamEventFlagUserDropped) {
|
||||||
|
list.error("Events were dropped by the FSEvents client. File system must be re-scanned.");
|
||||||
|
} else if (eventFlags[i] & kFSEventStreamEventFlagKernelDropped) {
|
||||||
|
list.error("Events were dropped by the kernel. File system must be re-scanned.");
|
||||||
|
} else {
|
||||||
|
list.error("Too many events. File system must be re-scanned.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDone) {
|
||||||
|
watcher->notify();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ignoredFlags = IGNORED_FLAGS;
|
||||||
|
if (__builtin_available(macOS 10.13, *)) {
|
||||||
|
ignoredFlags |= kFSEventStreamEventFlagItemCloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't care about any of the flags that are set, ignore this event.
|
||||||
|
if ((eventFlags[i] & ~ignoredFlags) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FSEvents exclusion paths only apply to files, not directories.
|
||||||
|
if (watcher->isIgnored(paths[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle unambiguous events first
|
||||||
|
if (isCreated && !(isRemoved || isModified || isRenamed)) {
|
||||||
|
state->tree->add(paths[i], 0, isDir);
|
||||||
|
list.create(paths[i]);
|
||||||
|
} else if (isRemoved && !(isCreated || isModified || isRenamed)) {
|
||||||
|
state->tree->remove(paths[i]);
|
||||||
|
list.remove(paths[i]);
|
||||||
|
if (paths[i] == watcher->mDir) {
|
||||||
|
deletedRoot = true;
|
||||||
|
}
|
||||||
|
} else if (isModified && !(isCreated || isRemoved || isRenamed)) {
|
||||||
|
struct stat file;
|
||||||
|
if (stat(paths[i], &file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore if mtime is the same as the last event.
|
||||||
|
// This prevents duplicate events from being emitted.
|
||||||
|
// If tv_nsec is zero, the file system probably only has second-level
|
||||||
|
// granularity so allow the even through in that case.
|
||||||
|
uint64_t mtime = CONVERT_TIME(file.st_mtimespec);
|
||||||
|
DirEntry *entry = state->tree->find(paths[i]);
|
||||||
|
if (entry && mtime == entry->mtime && file.st_mtimespec.tv_nsec != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
// Update mtime.
|
||||||
|
entry->mtime = mtime;
|
||||||
|
} else {
|
||||||
|
// Add to tree if this path has not been discovered yet.
|
||||||
|
state->tree->add(paths[i], mtime, S_ISDIR(file.st_mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
list.update(paths[i]);
|
||||||
|
} else {
|
||||||
|
// If multiple flags were set, then we need to call `stat` to determine if the file really exists.
|
||||||
|
// This helps disambiguate creates, updates, and deletes.
|
||||||
|
struct stat file;
|
||||||
|
if (stat(paths[i], &file) || !pathExists(paths[i])) {
|
||||||
|
// File does not exist, so we have to assume it was removed. This is not exact since the
|
||||||
|
// flags set by fsevents get coalesced together (e.g. created & deleted), so there is no way to
|
||||||
|
// know whether the create and delete both happened since our snapshot (in which case
|
||||||
|
// we'd rather ignore this event completely). This will result in some extra delete events
|
||||||
|
// being emitted for files we don't know about, but that is the best we can do.
|
||||||
|
state->tree->remove(paths[i]);
|
||||||
|
list.remove(paths[i]);
|
||||||
|
if (paths[i] == watcher->mDir) {
|
||||||
|
deletedRoot = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the file was modified, and existed before, then this is an update, otherwise a create.
|
||||||
|
uint64_t ctime = CONVERT_TIME(file.st_birthtimespec);
|
||||||
|
uint64_t mtime = CONVERT_TIME(file.st_mtimespec);
|
||||||
|
DirEntry *entry = !since ? state->tree->find(paths[i]) : NULL;
|
||||||
|
if (entry && entry->mtime == mtime && file.st_mtimespec.tv_nsec != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some mounted file systems report a creation time of 0/unix epoch which we special case.
|
||||||
|
if (isModified && (entry || (ctime <= since && ctime != 0))) {
|
||||||
|
state->tree->update(paths[i], mtime);
|
||||||
|
list.update(paths[i]);
|
||||||
|
} else {
|
||||||
|
state->tree->add(paths[i], mtime, S_ISDIR(file.st_mode));
|
||||||
|
list.create(paths[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!since) {
|
||||||
|
watcher->notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop watching if the root directory was deleted.
|
||||||
|
if (deletedRoot) {
|
||||||
|
stopStream((FSEventStreamRef)streamRef, CFRunLoopGetCurrent());
|
||||||
|
watcher->state = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkWatcher(WatcherRef watcher) {
|
||||||
|
struct stat file;
|
||||||
|
if (stat(watcher->mDir.c_str(), &file)) {
|
||||||
|
throw WatcherError(strerror(errno), watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISDIR(file.st_mode)) {
|
||||||
|
throw WatcherError(strerror(ENOTDIR), watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSEventsBackend::startStream(WatcherRef watcher, FSEventStreamEventId id) {
|
||||||
|
checkWatcher(watcher);
|
||||||
|
|
||||||
|
CFAbsoluteTime latency = 0.001;
|
||||||
|
CFStringRef fileWatchPath = CFStringCreateWithCString(
|
||||||
|
NULL,
|
||||||
|
watcher->mDir.c_str(),
|
||||||
|
kCFStringEncodingUTF8
|
||||||
|
);
|
||||||
|
|
||||||
|
CFArrayRef pathsToWatch = CFArrayCreate(
|
||||||
|
NULL,
|
||||||
|
(const void **)&fileWatchPath,
|
||||||
|
1,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make a watcher reference we can pass into the callback. This ensures bumped ref-count.
|
||||||
|
std::shared_ptr<Watcher>* callbackWatcher = new std::shared_ptr<Watcher> (watcher);
|
||||||
|
FSEventStreamContext callbackInfo {0, static_cast<void*> (callbackWatcher), nullptr, nullptr, nullptr};
|
||||||
|
FSEventStreamRef stream = FSEventStreamCreate(
|
||||||
|
NULL,
|
||||||
|
&FSEventsCallback,
|
||||||
|
&callbackInfo,
|
||||||
|
pathsToWatch,
|
||||||
|
id,
|
||||||
|
latency,
|
||||||
|
kFSEventStreamCreateFlagFileEvents
|
||||||
|
);
|
||||||
|
|
||||||
|
CFMutableArrayRef exclusions = CFArrayCreateMutable(NULL, watcher->mIgnorePaths.size(), NULL);
|
||||||
|
for (auto it = watcher->mIgnorePaths.begin(); it != watcher->mIgnorePaths.end(); it++) {
|
||||||
|
CFStringRef path = CFStringCreateWithCString(
|
||||||
|
NULL,
|
||||||
|
it->c_str(),
|
||||||
|
kCFStringEncodingUTF8
|
||||||
|
);
|
||||||
|
|
||||||
|
CFArrayAppendValue(exclusions, (const void *)path);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSEventStreamSetExclusionPaths(stream, exclusions);
|
||||||
|
|
||||||
|
FSEventStreamScheduleWithRunLoop(stream, mRunLoop, kCFRunLoopDefaultMode);
|
||||||
|
bool started = FSEventStreamStart(stream);
|
||||||
|
|
||||||
|
CFRelease(pathsToWatch);
|
||||||
|
CFRelease(fileWatchPath);
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
FSEventStreamRelease(stream);
|
||||||
|
throw WatcherError("Error starting FSEvents stream", watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto stateGuard = watcher->state;
|
||||||
|
State* s = static_cast<State*>(stateGuard.get());
|
||||||
|
s->tree = std::make_shared<DirTree>(watcher->mDir);
|
||||||
|
s->stream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSEventsBackend::start() {
|
||||||
|
mRunLoop = CFRunLoopGetCurrent();
|
||||||
|
CFRetain(mRunLoop);
|
||||||
|
|
||||||
|
// Unlock once run loop has started.
|
||||||
|
CFRunLoopPerformBlock(mRunLoop, kCFRunLoopDefaultMode, ^ {
|
||||||
|
notifyStarted();
|
||||||
|
});
|
||||||
|
|
||||||
|
CFRunLoopWakeUp(mRunLoop);
|
||||||
|
CFRunLoopRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
FSEventsBackend::~FSEventsBackend() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
CFRunLoopStop(mRunLoop);
|
||||||
|
CFRelease(mRunLoop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSEventsBackend::writeSnapshot(WatcherRef watcher, std::string *snapshotPath) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
checkWatcher(watcher);
|
||||||
|
|
||||||
|
FSEventStreamEventId id = FSEventsGetCurrentEventId();
|
||||||
|
std::ofstream ofs(*snapshotPath);
|
||||||
|
ofs << id;
|
||||||
|
ofs << "\n";
|
||||||
|
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
ofs << CONVERT_TIME(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSEventsBackend::getEventsSince(WatcherRef watcher, std::string *snapshotPath) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
std::ifstream ifs(*snapshotPath);
|
||||||
|
if (ifs.fail()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSEventStreamEventId id;
|
||||||
|
uint64_t since;
|
||||||
|
ifs >> id;
|
||||||
|
ifs >> since;
|
||||||
|
|
||||||
|
auto s = std::make_shared<State>();
|
||||||
|
s->since = since;
|
||||||
|
watcher->state = s;
|
||||||
|
|
||||||
|
startStream(watcher, id);
|
||||||
|
watcher->wait();
|
||||||
|
stopStream(s->stream, mRunLoop);
|
||||||
|
|
||||||
|
watcher->state = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called by Backend::watch which takes a lock on mMutex
|
||||||
|
void FSEventsBackend::subscribe(WatcherRef watcher) {
|
||||||
|
auto s = std::make_shared<State>();
|
||||||
|
s->since = 0;
|
||||||
|
watcher->state = s;
|
||||||
|
startStream(watcher, kFSEventStreamEventIdSinceNow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called by Backend::unwatch which takes a lock on mMutex
|
||||||
|
void FSEventsBackend::unsubscribe(WatcherRef watcher) {
|
||||||
|
auto stateGuard = watcher->state;
|
||||||
|
State* s = static_cast<State*>(stateGuard.get());
|
||||||
|
if (s != nullptr) {
|
||||||
|
stopStream(s->stream, mRunLoop);
|
||||||
|
watcher->state = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
node_modules/@parcel/watcher/src/macos/FSEventsBackend.hh
generated
vendored
Normal file
20
node_modules/@parcel/watcher/src/macos/FSEventsBackend.hh
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef FS_EVENTS_H
|
||||||
|
#define FS_EVENTS_H
|
||||||
|
|
||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
#include "../Backend.hh"
|
||||||
|
|
||||||
|
class FSEventsBackend : public Backend {
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
~FSEventsBackend();
|
||||||
|
void writeSnapshot(WatcherRef watcher, std::string *snapshotPath) override;
|
||||||
|
void getEventsSince(WatcherRef watcher, std::string *snapshotPath) override;
|
||||||
|
void subscribe(WatcherRef watcher) override;
|
||||||
|
void unsubscribe(WatcherRef watcher) override;
|
||||||
|
private:
|
||||||
|
void startStream(WatcherRef watcher, FSEventStreamEventId id);
|
||||||
|
CFRunLoopRef mRunLoop;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
41
node_modules/@parcel/watcher/src/shared/BruteForceBackend.cc
generated
vendored
Normal file
41
node_modules/@parcel/watcher/src/shared/BruteForceBackend.cc
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include <string>
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
#include "../Event.hh"
|
||||||
|
#include "./BruteForceBackend.hh"
|
||||||
|
|
||||||
|
std::shared_ptr<DirTree> BruteForceBackend::getTree(WatcherRef watcher, bool shouldRead) {
|
||||||
|
auto tree = DirTree::getCached(watcher->mDir);
|
||||||
|
|
||||||
|
// If the tree is not complete, read it if needed.
|
||||||
|
if (!tree->isComplete && shouldRead) {
|
||||||
|
readTree(watcher, tree);
|
||||||
|
tree->isComplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BruteForceBackend::writeSnapshot(WatcherRef watcher, std::string *snapshotPath) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
auto tree = getTree(watcher);
|
||||||
|
FILE *f = fopen(snapshotPath->c_str(), "w");
|
||||||
|
if (!f) {
|
||||||
|
throw std::runtime_error(std::string("Unable to open snapshot file: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->write(f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BruteForceBackend::getEventsSince(WatcherRef watcher, std::string *snapshotPath) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
FILE *f = fopen(snapshotPath->c_str(), "r");
|
||||||
|
if (!f) {
|
||||||
|
throw std::runtime_error(std::string("Unable to open snapshot file: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
DirTree snapshot{watcher->mDir, f};
|
||||||
|
auto now = getTree(watcher);
|
||||||
|
now->getChanges(&snapshot, watcher->mEvents);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
25
node_modules/@parcel/watcher/src/shared/BruteForceBackend.hh
generated
vendored
Normal file
25
node_modules/@parcel/watcher/src/shared/BruteForceBackend.hh
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef BRUTE_FORCE_H
|
||||||
|
#define BRUTE_FORCE_H
|
||||||
|
|
||||||
|
#include "../Backend.hh"
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
#include "../Watcher.hh"
|
||||||
|
|
||||||
|
class BruteForceBackend : public Backend {
|
||||||
|
public:
|
||||||
|
void writeSnapshot(WatcherRef watcher, std::string *snapshotPath) override;
|
||||||
|
void getEventsSince(WatcherRef watcher, std::string *snapshotPath) override;
|
||||||
|
void subscribe(WatcherRef watcher) override {
|
||||||
|
throw "Brute force backend doesn't support subscriptions.";
|
||||||
|
}
|
||||||
|
|
||||||
|
void unsubscribe(WatcherRef watcher) override {
|
||||||
|
throw "Brute force backend doesn't support subscriptions.";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<DirTree> getTree(WatcherRef watcher, bool shouldRead = true);
|
||||||
|
private:
|
||||||
|
void readTree(WatcherRef watcher, std::shared_ptr<DirTree> tree);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
50
node_modules/@parcel/watcher/src/unix/fts.cc
generated
vendored
Normal file
50
node_modules/@parcel/watcher/src/unix/fts.cc
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
// weird error on linux
|
||||||
|
#ifdef __THROW
|
||||||
|
#undef __THROW
|
||||||
|
#endif
|
||||||
|
#define __THROW
|
||||||
|
|
||||||
|
#include <fts.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
#include "../shared/BruteForceBackend.hh"
|
||||||
|
|
||||||
|
#define CONVERT_TIME(ts) ((uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec)
|
||||||
|
#if __APPLE__
|
||||||
|
#define st_mtim st_mtimespec
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void BruteForceBackend::readTree(WatcherRef watcher, std::shared_ptr<DirTree> tree) {
|
||||||
|
char *paths[2] {(char *)watcher->mDir.c_str(), NULL};
|
||||||
|
FTS *fts = fts_open(paths, FTS_NOCHDIR | FTS_PHYSICAL, NULL);
|
||||||
|
if (!fts) {
|
||||||
|
throw WatcherError(strerror(errno), watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
FTSENT *node;
|
||||||
|
bool isRoot = true;
|
||||||
|
|
||||||
|
while ((node = fts_read(fts)) != NULL) {
|
||||||
|
if (node->fts_errno) {
|
||||||
|
fts_close(fts);
|
||||||
|
throw WatcherError(strerror(node->fts_errno), watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRoot && !(node->fts_info & FTS_D)) {
|
||||||
|
fts_close(fts);
|
||||||
|
throw WatcherError(strerror(ENOTDIR), watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watcher->isIgnored(std::string(node->fts_path))) {
|
||||||
|
fts_set(fts, node, FTS_SKIP);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->add(node->fts_path, CONVERT_TIME(node->fts_statp->st_mtim), (node->fts_info & FTS_D) == FTS_D);
|
||||||
|
isRoot = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fts_close(fts);
|
||||||
|
}
|
||||||
77
node_modules/@parcel/watcher/src/unix/legacy.cc
generated
vendored
Normal file
77
node_modules/@parcel/watcher/src/unix/legacy.cc
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
// weird error on linux
|
||||||
|
#ifdef __THROW
|
||||||
|
#undef __THROW
|
||||||
|
#endif
|
||||||
|
#define __THROW
|
||||||
|
|
||||||
|
#ifdef _LIBC
|
||||||
|
# include <include/sys/stat.h>
|
||||||
|
#else
|
||||||
|
# include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
#include "../shared/BruteForceBackend.hh"
|
||||||
|
|
||||||
|
#define CONVERT_TIME(ts) ((uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec)
|
||||||
|
#if __APPLE__
|
||||||
|
#define st_mtim st_mtimespec
|
||||||
|
#endif
|
||||||
|
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
|
||||||
|
|
||||||
|
void iterateDir(WatcherRef watcher, const std::shared_ptr <DirTree> tree, const char *relative, int parent_fd, const std::string &dirname) {
|
||||||
|
int open_flags = (O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
|
||||||
|
int new_fd = openat(parent_fd, relative, open_flags);
|
||||||
|
if (new_fd == -1) {
|
||||||
|
if (errno == EACCES) {
|
||||||
|
return; // ignore insufficient permissions
|
||||||
|
}
|
||||||
|
|
||||||
|
throw WatcherError(strerror(errno), watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat rootAttributes;
|
||||||
|
fstatat(new_fd, ".", &rootAttributes, AT_SYMLINK_NOFOLLOW);
|
||||||
|
tree->add(dirname, CONVERT_TIME(rootAttributes.st_mtim), true);
|
||||||
|
|
||||||
|
if (DIR *dir = fdopendir(new_fd)) {
|
||||||
|
while (struct dirent *ent = (errno = 0, readdir(dir))) {
|
||||||
|
if (ISDOT(ent->d_name)) continue;
|
||||||
|
|
||||||
|
std::string fullPath = dirname + "/" + ent->d_name;
|
||||||
|
|
||||||
|
if (!watcher->isIgnored(fullPath)) {
|
||||||
|
struct stat attrib;
|
||||||
|
fstatat(new_fd, ent->d_name, &attrib, AT_SYMLINK_NOFOLLOW);
|
||||||
|
bool isDir = ent->d_type == DT_DIR;
|
||||||
|
|
||||||
|
if (isDir) {
|
||||||
|
iterateDir(watcher, tree, ent->d_name, new_fd, fullPath);
|
||||||
|
} else {
|
||||||
|
tree->add(fullPath, CONVERT_TIME(attrib.st_mtim), isDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
} else {
|
||||||
|
close(new_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno) {
|
||||||
|
throw WatcherError(strerror(errno), watcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BruteForceBackend::readTree(WatcherRef watcher, std::shared_ptr <DirTree> tree) {
|
||||||
|
int fd = open(watcher->mDir.c_str(), O_RDONLY);
|
||||||
|
if (fd) {
|
||||||
|
iterateDir(watcher, tree, ".", fd, watcher->mDir);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
132
node_modules/@parcel/watcher/src/wasm/WasmBackend.cc
generated
vendored
Normal file
132
node_modules/@parcel/watcher/src/wasm/WasmBackend.cc
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#include <sys/stat.h>
|
||||||
|
#include "WasmBackend.hh"
|
||||||
|
|
||||||
|
#define CONVERT_TIME(ts) ((uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec)
|
||||||
|
|
||||||
|
void WasmBackend::start() {
|
||||||
|
notifyStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WasmBackend::subscribe(WatcherRef watcher) {
|
||||||
|
// Build a full directory tree recursively, and watch each directory.
|
||||||
|
std::shared_ptr<DirTree> tree = getTree(watcher);
|
||||||
|
|
||||||
|
for (auto it = tree->entries.begin(); it != tree->entries.end(); it++) {
|
||||||
|
if (it->second.isDir) {
|
||||||
|
watchDir(watcher, it->second.path, tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WasmBackend::watchDir(WatcherRef watcher, std::string path, std::shared_ptr<DirTree> tree) {
|
||||||
|
int wd = wasm_backend_add_watch(path.c_str(), (void *)this);
|
||||||
|
std::shared_ptr<WasmSubscription> sub = std::make_shared<WasmSubscription>();
|
||||||
|
sub->tree = tree;
|
||||||
|
sub->path = path;
|
||||||
|
sub->watcher = watcher;
|
||||||
|
mSubscriptions.emplace(wd, sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void wasm_backend_event_handler(void *backend, int wd, int type, char *filename) {
|
||||||
|
WasmBackend *b = (WasmBackend *)(backend);
|
||||||
|
b->handleEvent(wd, type, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WasmBackend::handleEvent(int wd, int type, char *filename) {
|
||||||
|
// Find the subscriptions for this watch descriptor
|
||||||
|
auto range = mSubscriptions.equal_range(wd);
|
||||||
|
std::unordered_set<std::shared_ptr<WasmSubscription>> set;
|
||||||
|
for (auto it = range.first; it != range.second; it++) {
|
||||||
|
set.insert(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = set.begin(); it != set.end(); it++) {
|
||||||
|
if (handleSubscription(type, filename, *it)) {
|
||||||
|
(*it)->watcher->notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WasmBackend::handleSubscription(int type, char *filename, std::shared_ptr<WasmSubscription> sub) {
|
||||||
|
// Build full path and check if its in our ignore list.
|
||||||
|
WatcherRef watcher = sub->watcher;
|
||||||
|
std::string path = std::string(sub->path);
|
||||||
|
|
||||||
|
if (filename[0] != '\0') {
|
||||||
|
path += "/" + std::string(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watcher->isIgnored(path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == 1) {
|
||||||
|
struct stat st;
|
||||||
|
stat(path.c_str(), &st);
|
||||||
|
sub->tree->update(path, CONVERT_TIME(st.st_mtim));
|
||||||
|
watcher->mEvents.update(path);
|
||||||
|
} else if (type == 2) {
|
||||||
|
// Determine if this is a create or delete depending on if the file exists or not.
|
||||||
|
struct stat st;
|
||||||
|
if (lstat(path.c_str(), &st)) {
|
||||||
|
// If the entry being deleted/moved is a directory, remove it from the list of subscriptions
|
||||||
|
DirEntry *entry = sub->tree->find(path);
|
||||||
|
if (!entry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->isDir) {
|
||||||
|
std::string pathStart = path + DIR_SEP;
|
||||||
|
for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
|
||||||
|
if (it->second->path == path || it->second->path.rfind(pathStart, 0) == 0) {
|
||||||
|
wasm_backend_remove_watch(it->first);
|
||||||
|
it = mSubscriptions.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all sub-entries
|
||||||
|
for (auto it = sub->tree->entries.begin(); it != sub->tree->entries.end();) {
|
||||||
|
if (it->first.rfind(pathStart, 0) == 0) {
|
||||||
|
watcher->mEvents.remove(it->first);
|
||||||
|
it = sub->tree->entries.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher->mEvents.remove(path);
|
||||||
|
sub->tree->remove(path);
|
||||||
|
} else if (sub->tree->find(path)) {
|
||||||
|
sub->tree->update(path, CONVERT_TIME(st.st_mtim));
|
||||||
|
watcher->mEvents.update(path);
|
||||||
|
} else {
|
||||||
|
watcher->mEvents.create(path);
|
||||||
|
|
||||||
|
// If this is a create, check if it's a directory and start watching if it is.
|
||||||
|
DirEntry *entry = sub->tree->add(path, CONVERT_TIME(st.st_mtim), S_ISDIR(st.st_mode));
|
||||||
|
if (entry->isDir) {
|
||||||
|
watchDir(watcher, path, sub->tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WasmBackend::unsubscribe(WatcherRef watcher) {
|
||||||
|
// Find any subscriptions pointing to this watcher, and remove them.
|
||||||
|
for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
|
||||||
|
if (it->second->watcher.get() == watcher.get()) {
|
||||||
|
if (mSubscriptions.count(it->first) == 1) {
|
||||||
|
wasm_backend_remove_watch(it->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
it = mSubscriptions.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
node_modules/@parcel/watcher/src/wasm/WasmBackend.hh
generated
vendored
Normal file
34
node_modules/@parcel/watcher/src/wasm/WasmBackend.hh
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef WASM_H
|
||||||
|
#define WASM_H
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "../shared/BruteForceBackend.hh"
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
int wasm_backend_add_watch(const char *filename, void *backend);
|
||||||
|
void wasm_backend_remove_watch(int wd);
|
||||||
|
void wasm_backend_event_handler(void *backend, int wd, int type, char *filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WasmSubscription {
|
||||||
|
std::shared_ptr<DirTree> tree;
|
||||||
|
std::string path;
|
||||||
|
WatcherRef watcher;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WasmBackend : public BruteForceBackend {
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
void subscribe(WatcherRef watcher) override;
|
||||||
|
void unsubscribe(WatcherRef watcher) override;
|
||||||
|
void handleEvent(int wd, int type, char *filename);
|
||||||
|
private:
|
||||||
|
int mWasm;
|
||||||
|
std::unordered_multimap<int, std::shared_ptr<WasmSubscription>> mSubscriptions;
|
||||||
|
|
||||||
|
void watchDir(WatcherRef watcher, std::string path, std::shared_ptr<DirTree> tree);
|
||||||
|
bool handleSubscription(int type, char *filename, std::shared_ptr<WasmSubscription> sub);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
74
node_modules/@parcel/watcher/src/wasm/include.h
generated
vendored
Normal file
74
node_modules/@parcel/watcher/src/wasm/include.h
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright Node.js contributors. All rights reserved.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Node does not include the headers for these functions when compiling for WASM, so add them here.
|
||||||
|
#ifdef __wasm32__
|
||||||
|
extern "C" {
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL
|
||||||
|
napi_create_threadsafe_function(napi_env env,
|
||||||
|
napi_value func,
|
||||||
|
napi_value async_resource,
|
||||||
|
napi_value async_resource_name,
|
||||||
|
size_t max_queue_size,
|
||||||
|
size_t initial_thread_count,
|
||||||
|
void* thread_finalize_data,
|
||||||
|
napi_finalize thread_finalize_cb,
|
||||||
|
void* context,
|
||||||
|
napi_threadsafe_function_call_js call_js_cb,
|
||||||
|
napi_threadsafe_function* result);
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL napi_get_threadsafe_function_context(
|
||||||
|
napi_threadsafe_function func, void** result);
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL
|
||||||
|
napi_call_threadsafe_function(napi_threadsafe_function func,
|
||||||
|
void* data,
|
||||||
|
napi_threadsafe_function_call_mode is_blocking);
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL
|
||||||
|
napi_acquire_threadsafe_function(napi_threadsafe_function func);
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL napi_release_threadsafe_function(
|
||||||
|
napi_threadsafe_function func, napi_threadsafe_function_release_mode mode);
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL
|
||||||
|
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func);
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL
|
||||||
|
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func);
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL
|
||||||
|
napi_create_async_work(napi_env env,
|
||||||
|
napi_value async_resource,
|
||||||
|
napi_value async_resource_name,
|
||||||
|
napi_async_execute_callback execute,
|
||||||
|
napi_async_complete_callback complete,
|
||||||
|
void* data,
|
||||||
|
napi_async_work* result);
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL napi_delete_async_work(napi_env env,
|
||||||
|
napi_async_work work);
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL napi_queue_async_work(napi_env env,
|
||||||
|
napi_async_work work);
|
||||||
|
NAPI_EXTERN napi_status NAPI_CDECL napi_cancel_async_work(napi_env env,
|
||||||
|
napi_async_work work);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
302
node_modules/@parcel/watcher/src/watchman/BSER.cc
generated
vendored
Normal file
302
node_modules/@parcel/watcher/src/watchman/BSER.cc
generated
vendored
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include "./BSER.hh"
|
||||||
|
|
||||||
|
BSERType decodeType(std::istream &iss) {
|
||||||
|
int8_t type;
|
||||||
|
iss.read(reinterpret_cast<char*>(&type), sizeof(type));
|
||||||
|
return (BSERType) type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void expectType(std::istream &iss, BSERType expected) {
|
||||||
|
BSERType got = decodeType(iss);
|
||||||
|
if (got != expected) {
|
||||||
|
throw std::runtime_error("Unexpected BSER type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodeType(std::ostream &oss, BSERType type) {
|
||||||
|
int8_t t = (int8_t)type;
|
||||||
|
oss.write(reinterpret_cast<char*>(&t), sizeof(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Value : public BSERValue {
|
||||||
|
public:
|
||||||
|
T value;
|
||||||
|
Value(T val) {
|
||||||
|
value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERInteger : public Value<int64_t> {
|
||||||
|
public:
|
||||||
|
BSERInteger(int64_t value) : Value(value) {}
|
||||||
|
BSERInteger(std::istream &iss) {
|
||||||
|
int8_t int8;
|
||||||
|
int16_t int16;
|
||||||
|
int32_t int32;
|
||||||
|
int64_t int64;
|
||||||
|
|
||||||
|
BSERType type = decodeType(iss);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case BSER_INT8:
|
||||||
|
iss.read(reinterpret_cast<char*>(&int8), sizeof(int8));
|
||||||
|
value = int8;
|
||||||
|
break;
|
||||||
|
case BSER_INT16:
|
||||||
|
iss.read(reinterpret_cast<char*>(&int16), sizeof(int16));
|
||||||
|
value = int16;
|
||||||
|
break;
|
||||||
|
case BSER_INT32:
|
||||||
|
iss.read(reinterpret_cast<char*>(&int32), sizeof(int32));
|
||||||
|
value = int32;
|
||||||
|
break;
|
||||||
|
case BSER_INT64:
|
||||||
|
iss.read(reinterpret_cast<char*>(&int64), sizeof(int64));
|
||||||
|
value = int64;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Invalid BSER int type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t intValue() override {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode(std::ostream &oss) override {
|
||||||
|
if (value <= INT8_MAX) {
|
||||||
|
encodeType(oss, BSER_INT8);
|
||||||
|
int8_t v = (int8_t)value;
|
||||||
|
oss.write(reinterpret_cast<char*>(&v), sizeof(v));
|
||||||
|
} else if (value <= INT16_MAX) {
|
||||||
|
encodeType(oss, BSER_INT16);
|
||||||
|
int16_t v = (int16_t)value;
|
||||||
|
oss.write(reinterpret_cast<char*>(&v), sizeof(v));
|
||||||
|
} else if (value <= INT32_MAX) {
|
||||||
|
encodeType(oss, BSER_INT32);
|
||||||
|
int32_t v = (int32_t)value;
|
||||||
|
oss.write(reinterpret_cast<char*>(&v), sizeof(v));
|
||||||
|
} else {
|
||||||
|
encodeType(oss, BSER_INT64);
|
||||||
|
oss.write(reinterpret_cast<char*>(&value), sizeof(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERArray : public Value<BSER::Array> {
|
||||||
|
public:
|
||||||
|
BSERArray() : Value() {}
|
||||||
|
BSERArray(BSER::Array value) : Value(value) {}
|
||||||
|
BSERArray(std::istream &iss) {
|
||||||
|
expectType(iss, BSER_ARRAY);
|
||||||
|
int64_t len = BSERInteger(iss).intValue();
|
||||||
|
for (int64_t i = 0; i < len; i++) {
|
||||||
|
value.push_back(BSER(iss));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BSER::Array arrayValue() override {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode(std::ostream &oss) override {
|
||||||
|
encodeType(oss, BSER_ARRAY);
|
||||||
|
BSERInteger(value.size()).encode(oss);
|
||||||
|
for (auto it = value.begin(); it != value.end(); it++) {
|
||||||
|
it->encode(oss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERString : public Value<std::string> {
|
||||||
|
public:
|
||||||
|
BSERString(std::string value) : Value(value) {}
|
||||||
|
BSERString(std::istream &iss) {
|
||||||
|
expectType(iss, BSER_STRING);
|
||||||
|
int64_t len = BSERInteger(iss).intValue();
|
||||||
|
value.resize(len);
|
||||||
|
iss.read(&value[0], len);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string stringValue() override {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode(std::ostream &oss) override {
|
||||||
|
encodeType(oss, BSER_STRING);
|
||||||
|
BSERInteger(value.size()).encode(oss);
|
||||||
|
oss << value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERObject : public Value<BSER::Object> {
|
||||||
|
public:
|
||||||
|
BSERObject() : Value() {}
|
||||||
|
BSERObject(BSER::Object value) : Value(value) {}
|
||||||
|
BSERObject(std::istream &iss) {
|
||||||
|
expectType(iss, BSER_OBJECT);
|
||||||
|
int64_t len = BSERInteger(iss).intValue();
|
||||||
|
for (int64_t i = 0; i < len; i++) {
|
||||||
|
auto key = BSERString(iss).stringValue();
|
||||||
|
auto val = BSER(iss);
|
||||||
|
value.emplace(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BSER::Object objectValue() override {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode(std::ostream &oss) override {
|
||||||
|
encodeType(oss, BSER_OBJECT);
|
||||||
|
BSERInteger(value.size()).encode(oss);
|
||||||
|
for (auto it = value.begin(); it != value.end(); it++) {
|
||||||
|
BSERString(it->first).encode(oss);
|
||||||
|
it->second.encode(oss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERDouble : public Value<double> {
|
||||||
|
public:
|
||||||
|
BSERDouble(double value) : Value(value) {}
|
||||||
|
BSERDouble(std::istream &iss) {
|
||||||
|
expectType(iss, BSER_REAL);
|
||||||
|
iss.read(reinterpret_cast<char*>(&value), sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
double doubleValue() override {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode(std::ostream &oss) override {
|
||||||
|
encodeType(oss, BSER_REAL);
|
||||||
|
oss.write(reinterpret_cast<char*>(&value), sizeof(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERBoolean : public Value<bool> {
|
||||||
|
public:
|
||||||
|
BSERBoolean(bool value) : Value(value) {}
|
||||||
|
bool boolValue() override { return value; }
|
||||||
|
void encode(std::ostream &oss) override {
|
||||||
|
int8_t t = value == true ? static_cast<int8_t>(BSER_BOOL_TRUE) : static_cast<int8_t>(BSER_BOOL_FALSE);
|
||||||
|
oss.write(reinterpret_cast<char*>(&t), sizeof(t));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERNull : public Value<bool> {
|
||||||
|
public:
|
||||||
|
BSERNull() : Value(false) {}
|
||||||
|
void encode(std::ostream &oss) override {
|
||||||
|
encodeType(oss, BSER_NULL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<BSERArray> decodeTemplate(std::istream &iss) {
|
||||||
|
expectType(iss, BSER_TEMPLATE);
|
||||||
|
auto keys = BSERArray(iss).arrayValue();
|
||||||
|
auto len = BSERInteger(iss).intValue();
|
||||||
|
std::shared_ptr<BSERArray> arr = std::make_shared<BSERArray>();
|
||||||
|
for (int64_t i = 0; i < len; i++) {
|
||||||
|
BSER::Object obj;
|
||||||
|
for (auto it = keys.begin(); it != keys.end(); it++) {
|
||||||
|
if (iss.peek() == 0x0c) {
|
||||||
|
iss.ignore(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto val = BSER(iss);
|
||||||
|
obj.emplace(it->stringValue(), val);
|
||||||
|
}
|
||||||
|
arr->value.push_back(obj);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSER::BSER(std::istream &iss) {
|
||||||
|
BSERType type = decodeType(iss);
|
||||||
|
iss.unget();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case BSER_ARRAY:
|
||||||
|
m_ptr = std::make_shared<BSERArray>(iss);
|
||||||
|
break;
|
||||||
|
case BSER_OBJECT:
|
||||||
|
m_ptr = std::make_shared<BSERObject>(iss);
|
||||||
|
break;
|
||||||
|
case BSER_STRING:
|
||||||
|
m_ptr = std::make_shared<BSERString>(iss);
|
||||||
|
break;
|
||||||
|
case BSER_INT8:
|
||||||
|
case BSER_INT16:
|
||||||
|
case BSER_INT32:
|
||||||
|
case BSER_INT64:
|
||||||
|
m_ptr = std::make_shared<BSERInteger>(iss);
|
||||||
|
break;
|
||||||
|
case BSER_REAL:
|
||||||
|
m_ptr = std::make_shared<BSERDouble>(iss);
|
||||||
|
break;
|
||||||
|
case BSER_BOOL_TRUE:
|
||||||
|
iss.ignore(1);
|
||||||
|
m_ptr = std::make_shared<BSERBoolean>(true);
|
||||||
|
break;
|
||||||
|
case BSER_BOOL_FALSE:
|
||||||
|
iss.ignore(1);
|
||||||
|
m_ptr = std::make_shared<BSERBoolean>(false);
|
||||||
|
break;
|
||||||
|
case BSER_NULL:
|
||||||
|
iss.ignore(1);
|
||||||
|
m_ptr = std::make_shared<BSERNull>();
|
||||||
|
break;
|
||||||
|
case BSER_TEMPLATE:
|
||||||
|
m_ptr = decodeTemplate(iss);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("unknown BSER type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BSER::BSER() : m_ptr(std::make_shared<BSERNull>()) {}
|
||||||
|
BSER::BSER(BSER::Array value) : m_ptr(std::make_shared<BSERArray>(value)) {}
|
||||||
|
BSER::BSER(BSER::Object value) : m_ptr(std::make_shared<BSERObject>(value)) {}
|
||||||
|
BSER::BSER(const char *value) : m_ptr(std::make_shared<BSERString>(value)) {}
|
||||||
|
BSER::BSER(std::string value) : m_ptr(std::make_shared<BSERString>(value)) {}
|
||||||
|
BSER::BSER(int64_t value) : m_ptr(std::make_shared<BSERInteger>(value)) {}
|
||||||
|
BSER::BSER(double value) : m_ptr(std::make_shared<BSERDouble>(value)) {}
|
||||||
|
BSER::BSER(bool value) : m_ptr(std::make_shared<BSERBoolean>(value)) {}
|
||||||
|
|
||||||
|
BSER::Array BSER::arrayValue() { return m_ptr->arrayValue(); }
|
||||||
|
BSER::Object BSER::objectValue() { return m_ptr->objectValue(); }
|
||||||
|
std::string BSER::stringValue() { return m_ptr->stringValue(); }
|
||||||
|
int64_t BSER::intValue() { return m_ptr->intValue(); }
|
||||||
|
double BSER::doubleValue() { return m_ptr->doubleValue(); }
|
||||||
|
bool BSER::boolValue() { return m_ptr->boolValue(); }
|
||||||
|
void BSER::encode(std::ostream &oss) {
|
||||||
|
m_ptr->encode(oss);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t BSER::decodeLength(std::istream &iss) {
|
||||||
|
char pdu[2];
|
||||||
|
if (!iss.read(pdu, 2) || pdu[0] != 0 || pdu[1] != 1) {
|
||||||
|
throw std::runtime_error("Invalid BSER");
|
||||||
|
}
|
||||||
|
|
||||||
|
return BSERInteger(iss).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BSER::encode() {
|
||||||
|
std::ostringstream oss(std::ios_base::binary);
|
||||||
|
encode(oss);
|
||||||
|
|
||||||
|
std::ostringstream res(std::ios_base::binary);
|
||||||
|
res.write("\x00\x01", 2);
|
||||||
|
|
||||||
|
BSERInteger(oss.str().size()).encode(res);
|
||||||
|
res << oss.str();
|
||||||
|
return res.str();
|
||||||
|
}
|
||||||
69
node_modules/@parcel/watcher/src/watchman/BSER.hh
generated
vendored
Normal file
69
node_modules/@parcel/watcher/src/watchman/BSER.hh
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef BSER_H
|
||||||
|
#define BSER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
enum BSERType {
|
||||||
|
BSER_ARRAY = 0x00,
|
||||||
|
BSER_OBJECT = 0x01,
|
||||||
|
BSER_STRING = 0x02,
|
||||||
|
BSER_INT8 = 0x03,
|
||||||
|
BSER_INT16 = 0x04,
|
||||||
|
BSER_INT32 = 0x05,
|
||||||
|
BSER_INT64 = 0x06,
|
||||||
|
BSER_REAL = 0x07,
|
||||||
|
BSER_BOOL_TRUE = 0x08,
|
||||||
|
BSER_BOOL_FALSE = 0x09,
|
||||||
|
BSER_NULL = 0x0a,
|
||||||
|
BSER_TEMPLATE = 0x0b
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERValue;
|
||||||
|
|
||||||
|
class BSER {
|
||||||
|
public:
|
||||||
|
typedef std::vector<BSER> Array;
|
||||||
|
typedef std::unordered_map<std::string, BSER> Object;
|
||||||
|
|
||||||
|
BSER();
|
||||||
|
BSER(BSER::Array value);
|
||||||
|
BSER(BSER::Object value);
|
||||||
|
BSER(std::string value);
|
||||||
|
BSER(const char *value);
|
||||||
|
BSER(int64_t value);
|
||||||
|
BSER(double value);
|
||||||
|
BSER(bool value);
|
||||||
|
BSER(std::istream &iss);
|
||||||
|
|
||||||
|
BSER::Array arrayValue();
|
||||||
|
BSER::Object objectValue();
|
||||||
|
std::string stringValue();
|
||||||
|
int64_t intValue();
|
||||||
|
double doubleValue();
|
||||||
|
bool boolValue();
|
||||||
|
void encode(std::ostream &oss);
|
||||||
|
|
||||||
|
static int64_t decodeLength(std::istream &iss);
|
||||||
|
std::string encode();
|
||||||
|
private:
|
||||||
|
std::shared_ptr<BSERValue> m_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BSERValue {
|
||||||
|
protected:
|
||||||
|
friend class BSER;
|
||||||
|
virtual BSER::Array arrayValue() { return BSER::Array(); }
|
||||||
|
virtual BSER::Object objectValue() { return BSER::Object(); }
|
||||||
|
virtual std::string stringValue() { return std::string(); }
|
||||||
|
virtual int64_t intValue() { return 0; }
|
||||||
|
virtual double doubleValue() { return 0; }
|
||||||
|
virtual bool boolValue() { return false; }
|
||||||
|
virtual void encode(std::ostream &oss) {}
|
||||||
|
virtual ~BSERValue() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
175
node_modules/@parcel/watcher/src/watchman/IPC.hh
generated
vendored
Normal file
175
node_modules/@parcel/watcher/src/watchman/IPC.hh
generated
vendored
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
#ifndef IPC_H
|
||||||
|
#define IPC_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class IPC {
|
||||||
|
public:
|
||||||
|
IPC(std::string path) {
|
||||||
|
mStopped = false;
|
||||||
|
#ifdef _WIN32
|
||||||
|
while (true) {
|
||||||
|
mPipe = CreateFile(
|
||||||
|
path.data(), // pipe name
|
||||||
|
GENERIC_READ | GENERIC_WRITE, // read and write access
|
||||||
|
0, // no sharing
|
||||||
|
NULL, // default security attributes
|
||||||
|
OPEN_EXISTING, // opens existing pipe
|
||||||
|
FILE_FLAG_OVERLAPPED, // attributes
|
||||||
|
NULL // no template file
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mPipe != INVALID_HANDLE_VALUE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetLastError() != ERROR_PIPE_BUSY) {
|
||||||
|
throw std::runtime_error("Could not open pipe");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for pipe to become available if it is busy
|
||||||
|
if (!WaitNamedPipe(path.data(), 30000)) {
|
||||||
|
throw std::runtime_error("Error waiting for pipe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mReader = CreateEvent(NULL, true, false, NULL);
|
||||||
|
mWriter = CreateEvent(NULL, true, false, NULL);
|
||||||
|
#else
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
mSock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (connect(mSock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un))) {
|
||||||
|
throw std::runtime_error("Error connecting to socket");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
~IPC() {
|
||||||
|
mStopped = true;
|
||||||
|
#ifdef _WIN32
|
||||||
|
CancelIo(mPipe);
|
||||||
|
CloseHandle(mPipe);
|
||||||
|
CloseHandle(mReader);
|
||||||
|
CloseHandle(mWriter);
|
||||||
|
#else
|
||||||
|
shutdown(mSock, SHUT_RDWR);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(std::string buf) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
overlapped.hEvent = mWriter;
|
||||||
|
bool success = WriteFile(
|
||||||
|
mPipe, // pipe handle
|
||||||
|
buf.data(), // message
|
||||||
|
static_cast<DWORD>(buf.size()), // message length
|
||||||
|
NULL, // bytes written
|
||||||
|
&overlapped // overlapped
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mStopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
if (GetLastError() != ERROR_IO_PENDING) {
|
||||||
|
throw std::runtime_error("Write error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD written;
|
||||||
|
success = GetOverlappedResult(mPipe, &overlapped, &written, true);
|
||||||
|
if (!success) {
|
||||||
|
throw std::runtime_error("GetOverlappedResult failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (written != buf.size()) {
|
||||||
|
throw std::runtime_error("Wrong number of bytes written");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int r = 0;
|
||||||
|
for (unsigned int i = 0; i != buf.size(); i += r) {
|
||||||
|
r = ::write(mSock, &buf[i], buf.size() - i);
|
||||||
|
if (r == -1) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
r = 0;
|
||||||
|
} else if (mStopped) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Write error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int read(char *buf, size_t len) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
overlapped.hEvent = mReader;
|
||||||
|
bool success = ReadFile(
|
||||||
|
mPipe, // pipe handle
|
||||||
|
buf, // buffer to receive reply
|
||||||
|
static_cast<DWORD>(len), // size of buffer
|
||||||
|
NULL, // number of bytes read
|
||||||
|
&overlapped // overlapped
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success && !mStopped) {
|
||||||
|
if (GetLastError() != ERROR_IO_PENDING) {
|
||||||
|
throw std::runtime_error("Read error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD read = 0;
|
||||||
|
success = GetOverlappedResult(mPipe, &overlapped, &read, true);
|
||||||
|
if (!success && !mStopped) {
|
||||||
|
throw std::runtime_error("GetOverlappedResult failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
#else
|
||||||
|
int r = ::read(mSock, buf, len);
|
||||||
|
if (r == 0 && !mStopped) {
|
||||||
|
throw std::runtime_error("Socket ended unexpectedly");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
if (mStopped) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error(strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mStopped;
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE mPipe;
|
||||||
|
HANDLE mReader;
|
||||||
|
HANDLE mWriter;
|
||||||
|
#else
|
||||||
|
int mSock;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
342
node_modules/@parcel/watcher/src/watchman/WatchmanBackend.cc
generated
vendored
Normal file
342
node_modules/@parcel/watcher/src/watchman/WatchmanBackend.cc
generated
vendored
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
#include "../Event.hh"
|
||||||
|
#include "./BSER.hh"
|
||||||
|
#include "./WatchmanBackend.hh"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "../windows/win_utils.hh"
|
||||||
|
#define S_ISDIR(mode) ((mode & _S_IFDIR) == _S_IFDIR)
|
||||||
|
#define popen _popen
|
||||||
|
#define pclose _pclose
|
||||||
|
#else
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#define normalizePath(dir) dir
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
BSER readBSER(T &&do_read) {
|
||||||
|
std::stringstream oss;
|
||||||
|
char buffer[256];
|
||||||
|
size_t r;
|
||||||
|
int64_t len = -1;
|
||||||
|
do {
|
||||||
|
// Start by reading a minimal amount of data in order to decode the length.
|
||||||
|
// After that, attempt to read the remaining length, up to the buffer size.
|
||||||
|
r = do_read(buffer, len == -1 ? 20 : (len < 256 ? len : 256));
|
||||||
|
oss << std::string(buffer, r);
|
||||||
|
|
||||||
|
if (len == -1) {
|
||||||
|
uint64_t l = BSER::decodeLength(oss);
|
||||||
|
len = l + oss.tellg();
|
||||||
|
}
|
||||||
|
|
||||||
|
len -= r;
|
||||||
|
} while (len > 0);
|
||||||
|
|
||||||
|
return BSER(oss);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getSockPath() {
|
||||||
|
auto var = getenv("WATCHMAN_SOCK");
|
||||||
|
if (var && *var) {
|
||||||
|
return std::string(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
FILE *fp = popen("watchman --output-encoding=bser get-sockname", "r");
|
||||||
|
#else
|
||||||
|
FILE *fp = popen("watchman --output-encoding=bser get-sockname 2>/dev/null", "r");
|
||||||
|
#endif
|
||||||
|
if (fp == NULL || errno == ECHILD) {
|
||||||
|
throw std::runtime_error("Failed to execute watchman");
|
||||||
|
}
|
||||||
|
|
||||||
|
BSER b = readBSER([fp] (char *buf, size_t len) {
|
||||||
|
return fread(buf, sizeof(char), len, fp);
|
||||||
|
});
|
||||||
|
|
||||||
|
pclose(fp);
|
||||||
|
|
||||||
|
auto objValue = b.objectValue();
|
||||||
|
auto foundSockname = objValue.find("sockname");
|
||||||
|
if (foundSockname == objValue.end()) {
|
||||||
|
throw std::runtime_error("sockname not found");
|
||||||
|
}
|
||||||
|
return foundSockname->second.stringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<IPC> watchmanConnect() {
|
||||||
|
std::string path = getSockPath();
|
||||||
|
return std::unique_ptr<IPC>(new IPC(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
BSER watchmanRead(IPC *ipc) {
|
||||||
|
return readBSER([ipc] (char *buf, size_t len) {
|
||||||
|
return ipc->read(buf, len);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BSER::Object WatchmanBackend::watchmanRequest(BSER b) {
|
||||||
|
std::string cmd = b.encode();
|
||||||
|
mIPC->write(cmd);
|
||||||
|
mRequestSignal.notify();
|
||||||
|
|
||||||
|
mResponseSignal.wait();
|
||||||
|
mResponseSignal.reset();
|
||||||
|
|
||||||
|
if (!mError.empty()) {
|
||||||
|
std::runtime_error err = std::runtime_error(mError);
|
||||||
|
mError = std::string();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchmanBackend::watchmanWatch(std::string dir) {
|
||||||
|
std::vector<BSER> cmd;
|
||||||
|
cmd.push_back("watch");
|
||||||
|
cmd.push_back(normalizePath(dir));
|
||||||
|
watchmanRequest(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WatchmanBackend::checkAvailable() {
|
||||||
|
try {
|
||||||
|
watchmanConnect();
|
||||||
|
return true;
|
||||||
|
} catch (std::exception&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleFiles(WatcherRef watcher, BSER::Object obj) {
|
||||||
|
auto found = obj.find("files");
|
||||||
|
if (found == obj.end()) {
|
||||||
|
throw WatcherError("Error reading changes from watchman", watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto files = found->second.arrayValue();
|
||||||
|
for (auto it = files.begin(); it != files.end(); it++) {
|
||||||
|
auto file = it->objectValue();
|
||||||
|
auto name = file.find("name")->second.stringValue();
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::replace(name.begin(), name.end(), '/', '\\');
|
||||||
|
#endif
|
||||||
|
auto mode = file.find("mode")->second.intValue();
|
||||||
|
auto isNew = file.find("new")->second.boolValue();
|
||||||
|
auto exists = file.find("exists")->second.boolValue();
|
||||||
|
auto path = watcher->mDir + DIR_SEP + name;
|
||||||
|
if (watcher->isIgnored(path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNew && exists) {
|
||||||
|
watcher->mEvents.create(path);
|
||||||
|
} else if (exists && !S_ISDIR(mode)) {
|
||||||
|
watcher->mEvents.update(path);
|
||||||
|
} else if (!isNew && !exists) {
|
||||||
|
watcher->mEvents.remove(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchmanBackend::handleSubscription(BSER::Object obj) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
auto subscription = obj.find("subscription")->second.stringValue();
|
||||||
|
auto it = mSubscriptions.find(subscription);
|
||||||
|
if (it == mSubscriptions.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto watcher = it->second;
|
||||||
|
try {
|
||||||
|
handleFiles(watcher, obj);
|
||||||
|
watcher->notify();
|
||||||
|
} catch (WatcherError &err) {
|
||||||
|
handleWatcherError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchmanBackend::start() {
|
||||||
|
mIPC = watchmanConnect();
|
||||||
|
notifyStarted();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// If there are no subscriptions we are reading, wait for a request.
|
||||||
|
if (mSubscriptions.size() == 0) {
|
||||||
|
mRequestSignal.wait();
|
||||||
|
mRequestSignal.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break out of loop if we are stopped.
|
||||||
|
if (mStopped) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to read from the socket.
|
||||||
|
// If there is an error and we are stopped, break.
|
||||||
|
BSER b;
|
||||||
|
try {
|
||||||
|
b = watchmanRead(&*mIPC);
|
||||||
|
} catch (std::exception &err) {
|
||||||
|
if (mStopped) {
|
||||||
|
break;
|
||||||
|
} else if (mResponseSignal.isWaiting()) {
|
||||||
|
mError = err.what();
|
||||||
|
mResponseSignal.notify();
|
||||||
|
} else {
|
||||||
|
// Throwing causes the backend to be destroyed, but we never reach the code below to notify the signal
|
||||||
|
mEndedSignal.notify();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto obj = b.objectValue();
|
||||||
|
auto error = obj.find("error");
|
||||||
|
if (error != obj.end()) {
|
||||||
|
mError = error->second.stringValue();
|
||||||
|
mResponseSignal.notify();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this message is for a subscription, handle it, otherwise notify the request.
|
||||||
|
auto subscription = obj.find("subscription");
|
||||||
|
if (subscription != obj.end()) {
|
||||||
|
handleSubscription(obj);
|
||||||
|
} else {
|
||||||
|
mResponse = obj;
|
||||||
|
mResponseSignal.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mEndedSignal.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchmanBackend::~WatchmanBackend() {
|
||||||
|
// Mark the watcher as stopped, close the socket, and trigger the lock.
|
||||||
|
// This will cause the read loop to be broken and the thread to exit.
|
||||||
|
mStopped = true;
|
||||||
|
mIPC.reset();
|
||||||
|
mRequestSignal.notify();
|
||||||
|
|
||||||
|
// If not ended yet, wait.
|
||||||
|
mEndedSignal.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WatchmanBackend::clock(WatcherRef watcher) {
|
||||||
|
BSER::Array cmd;
|
||||||
|
cmd.push_back("clock");
|
||||||
|
cmd.push_back(normalizePath(watcher->mDir));
|
||||||
|
|
||||||
|
BSER::Object obj = watchmanRequest(cmd);
|
||||||
|
auto found = obj.find("clock");
|
||||||
|
if (found == obj.end()) {
|
||||||
|
throw WatcherError("Error reading clock from watchman", watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found->second.stringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchmanBackend::writeSnapshot(WatcherRef watcher, std::string *snapshotPath) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
watchmanWatch(watcher->mDir);
|
||||||
|
|
||||||
|
std::ofstream ofs(*snapshotPath);
|
||||||
|
ofs << clock(watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchmanBackend::getEventsSince(WatcherRef watcher, std::string *snapshotPath) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
std::ifstream ifs(*snapshotPath);
|
||||||
|
if (ifs.fail()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
watchmanWatch(watcher->mDir);
|
||||||
|
|
||||||
|
std::string clock;
|
||||||
|
ifs >> clock;
|
||||||
|
|
||||||
|
BSER::Array cmd;
|
||||||
|
cmd.push_back("since");
|
||||||
|
cmd.push_back(normalizePath(watcher->mDir));
|
||||||
|
cmd.push_back(clock);
|
||||||
|
|
||||||
|
BSER::Object obj = watchmanRequest(cmd);
|
||||||
|
handleFiles(watcher, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getId(WatcherRef watcher) {
|
||||||
|
std::ostringstream id;
|
||||||
|
id << "parcel-";
|
||||||
|
id << static_cast<void*>(watcher.get());
|
||||||
|
return id.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called by Backend::watch which takes a lock on mMutex
|
||||||
|
void WatchmanBackend::subscribe(WatcherRef watcher) {
|
||||||
|
watchmanWatch(watcher->mDir);
|
||||||
|
|
||||||
|
std::string id = getId(watcher);
|
||||||
|
BSER::Array cmd;
|
||||||
|
cmd.push_back("subscribe");
|
||||||
|
cmd.push_back(normalizePath(watcher->mDir));
|
||||||
|
cmd.push_back(id);
|
||||||
|
|
||||||
|
BSER::Array fields;
|
||||||
|
fields.push_back("name");
|
||||||
|
fields.push_back("mode");
|
||||||
|
fields.push_back("exists");
|
||||||
|
fields.push_back("new");
|
||||||
|
|
||||||
|
BSER::Object opts;
|
||||||
|
opts.emplace("fields", fields);
|
||||||
|
opts.emplace("since", clock(watcher));
|
||||||
|
|
||||||
|
if (watcher->mIgnorePaths.size() > 0) {
|
||||||
|
BSER::Array ignore;
|
||||||
|
BSER::Array anyOf;
|
||||||
|
anyOf.push_back("anyof");
|
||||||
|
|
||||||
|
for (auto it = watcher->mIgnorePaths.begin(); it != watcher->mIgnorePaths.end(); it++) {
|
||||||
|
std::string pathStart = watcher->mDir + DIR_SEP;
|
||||||
|
if (it->rfind(pathStart, 0) == 0) {
|
||||||
|
auto relative = it->substr(pathStart.size());
|
||||||
|
BSER::Array dirname;
|
||||||
|
dirname.push_back("dirname");
|
||||||
|
dirname.push_back(relative);
|
||||||
|
anyOf.push_back(dirname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ignore.push_back("not");
|
||||||
|
ignore.push_back(anyOf);
|
||||||
|
|
||||||
|
opts.emplace("expression", ignore);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.push_back(opts);
|
||||||
|
watchmanRequest(cmd);
|
||||||
|
|
||||||
|
mSubscriptions.emplace(id, watcher);
|
||||||
|
mRequestSignal.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called by Backend::unwatch which takes a lock on mMutex
|
||||||
|
void WatchmanBackend::unsubscribe(WatcherRef watcher) {
|
||||||
|
std::string id = getId(watcher);
|
||||||
|
auto erased = mSubscriptions.erase(id);
|
||||||
|
|
||||||
|
if (erased) {
|
||||||
|
BSER::Array cmd;
|
||||||
|
cmd.push_back("unsubscribe");
|
||||||
|
cmd.push_back(normalizePath(watcher->mDir));
|
||||||
|
cmd.push_back(id);
|
||||||
|
|
||||||
|
watchmanRequest(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
node_modules/@parcel/watcher/src/watchman/WatchmanBackend.hh
generated
vendored
Normal file
35
node_modules/@parcel/watcher/src/watchman/WatchmanBackend.hh
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef WATCHMAN_H
|
||||||
|
#define WATCHMAN_H
|
||||||
|
|
||||||
|
#include "../Backend.hh"
|
||||||
|
#include "./BSER.hh"
|
||||||
|
#include "../Signal.hh"
|
||||||
|
#include "./IPC.hh"
|
||||||
|
|
||||||
|
class WatchmanBackend : public Backend {
|
||||||
|
public:
|
||||||
|
static bool checkAvailable();
|
||||||
|
void start() override;
|
||||||
|
WatchmanBackend() : mStopped(false) {};
|
||||||
|
~WatchmanBackend();
|
||||||
|
void writeSnapshot(WatcherRef watcher, std::string *snapshotPath) override;
|
||||||
|
void getEventsSince(WatcherRef watcher, std::string *snapshotPath) override;
|
||||||
|
void subscribe(WatcherRef watcher) override;
|
||||||
|
void unsubscribe(WatcherRef watcher) override;
|
||||||
|
private:
|
||||||
|
std::unique_ptr<IPC> mIPC;
|
||||||
|
Signal mRequestSignal;
|
||||||
|
Signal mResponseSignal;
|
||||||
|
BSER::Object mResponse;
|
||||||
|
std::string mError;
|
||||||
|
std::unordered_map<std::string, WatcherRef> mSubscriptions;
|
||||||
|
bool mStopped;
|
||||||
|
Signal mEndedSignal;
|
||||||
|
|
||||||
|
std::string clock(WatcherRef watcher);
|
||||||
|
void watchmanWatch(std::string dir);
|
||||||
|
BSER::Object watchmanRequest(BSER cmd);
|
||||||
|
void handleSubscription(BSER::Object obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
282
node_modules/@parcel/watcher/src/windows/WindowsBackend.cc
generated
vendored
Normal file
282
node_modules/@parcel/watcher/src/windows/WindowsBackend.cc
generated
vendored
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
|
#include "../DirTree.hh"
|
||||||
|
#include "../shared/BruteForceBackend.hh"
|
||||||
|
#include "./WindowsBackend.hh"
|
||||||
|
#include "./win_utils.hh"
|
||||||
|
|
||||||
|
#define DEFAULT_BUF_SIZE 1024 * 1024
|
||||||
|
#define NETWORK_BUF_SIZE 64 * 1024
|
||||||
|
#define CONVERT_TIME(ft) ULARGE_INTEGER{ft.dwLowDateTime, ft.dwHighDateTime}.QuadPart
|
||||||
|
|
||||||
|
void BruteForceBackend::readTree(WatcherRef watcher, std::shared_ptr<DirTree> tree) {
|
||||||
|
std::stack<std::string> directories;
|
||||||
|
|
||||||
|
directories.push(watcher->mDir);
|
||||||
|
|
||||||
|
while (!directories.empty()) {
|
||||||
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
std::string path = directories.top();
|
||||||
|
std::string spec = path + "\\*";
|
||||||
|
directories.pop();
|
||||||
|
|
||||||
|
WIN32_FIND_DATA ffd;
|
||||||
|
hFind = FindFirstFile(spec.c_str(), &ffd);
|
||||||
|
|
||||||
|
if (hFind == INVALID_HANDLE_VALUE) {
|
||||||
|
if (path == watcher->mDir) {
|
||||||
|
FindClose(hFind);
|
||||||
|
throw WatcherError("Error opening directory", watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->remove(path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (strcmp(ffd.cFileName, ".") != 0 && strcmp(ffd.cFileName, "..") != 0) {
|
||||||
|
std::string fullPath = path + "\\" + ffd.cFileName;
|
||||||
|
if (watcher->isIgnored(fullPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->add(fullPath, CONVERT_TIME(ffd.ftLastWriteTime), ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
|
directories.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (FindNextFile(hFind, &ffd) != 0);
|
||||||
|
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsBackend::start() {
|
||||||
|
mRunning = true;
|
||||||
|
notifyStarted();
|
||||||
|
|
||||||
|
while (mRunning) {
|
||||||
|
SleepEx(INFINITE, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsBackend::~WindowsBackend() {
|
||||||
|
// Mark as stopped, and queue a noop function in the thread to break the loop
|
||||||
|
mRunning = false;
|
||||||
|
QueueUserAPC([](__in ULONG_PTR) {}, mThread.native_handle(), (ULONG_PTR)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Subscription: public WatcherState {
|
||||||
|
public:
|
||||||
|
Subscription(WindowsBackend *backend, WatcherRef watcher, std::shared_ptr<DirTree> tree) {
|
||||||
|
mRunning = true;
|
||||||
|
mBackend = backend;
|
||||||
|
mWatcher = watcher;
|
||||||
|
mTree = tree;
|
||||||
|
ZeroMemory(&mOverlapped, sizeof(OVERLAPPED));
|
||||||
|
mOverlapped.hEvent = this;
|
||||||
|
mReadBuffer.resize(DEFAULT_BUF_SIZE);
|
||||||
|
mWriteBuffer.resize(DEFAULT_BUF_SIZE);
|
||||||
|
|
||||||
|
mDirectoryHandle = CreateFileW(
|
||||||
|
utf8ToUtf16(watcher->mDir).data(),
|
||||||
|
FILE_LIST_DIRECTORY,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mDirectoryHandle == INVALID_HANDLE_VALUE) {
|
||||||
|
throw WatcherError("Invalid handle", mWatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the path is a directory
|
||||||
|
BY_HANDLE_FILE_INFORMATION info;
|
||||||
|
bool success = GetFileInformationByHandle(
|
||||||
|
mDirectoryHandle,
|
||||||
|
&info
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw WatcherError("Could not get file information", mWatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
throw WatcherError("Not a directory", mWatcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Subscription() {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
try {
|
||||||
|
poll();
|
||||||
|
} catch (WatcherError &err) {
|
||||||
|
mBackend->handleWatcherError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
if (mRunning) {
|
||||||
|
mRunning = false;
|
||||||
|
CancelIo(mDirectoryHandle);
|
||||||
|
CloseHandle(mDirectoryHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll() {
|
||||||
|
if (!mRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asynchronously wait for changes.
|
||||||
|
int success = ReadDirectoryChangesW(
|
||||||
|
mDirectoryHandle,
|
||||||
|
mWriteBuffer.data(),
|
||||||
|
static_cast<DWORD>(mWriteBuffer.size()),
|
||||||
|
TRUE, // recursive
|
||||||
|
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES
|
||||||
|
| FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||||
|
NULL,
|
||||||
|
&mOverlapped,
|
||||||
|
[](DWORD errorCode, DWORD numBytes, LPOVERLAPPED overlapped) {
|
||||||
|
auto subscription = reinterpret_cast<Subscription *>(overlapped->hEvent);
|
||||||
|
try {
|
||||||
|
subscription->processEvents(errorCode);
|
||||||
|
} catch (WatcherError &err) {
|
||||||
|
subscription->mBackend->handleWatcherError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw WatcherError("Failed to read changes", mWatcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processEvents(DWORD errorCode) {
|
||||||
|
if (!mRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (errorCode) {
|
||||||
|
case ERROR_OPERATION_ABORTED:
|
||||||
|
return;
|
||||||
|
case ERROR_INVALID_PARAMETER:
|
||||||
|
// resize buffers to network size (64kb), and try again
|
||||||
|
mReadBuffer.resize(NETWORK_BUF_SIZE);
|
||||||
|
mWriteBuffer.resize(NETWORK_BUF_SIZE);
|
||||||
|
poll();
|
||||||
|
return;
|
||||||
|
case ERROR_NOTIFY_ENUM_DIR:
|
||||||
|
throw WatcherError("Buffer overflow. Some events may have been lost.", mWatcher);
|
||||||
|
case ERROR_ACCESS_DENIED: {
|
||||||
|
// This can happen if the watched directory is deleted. Check if that is the case,
|
||||||
|
// and if so emit a delete event. Otherwise, fall through to default error case.
|
||||||
|
DWORD attrs = GetFileAttributesW(utf8ToUtf16(mWatcher->mDir).data());
|
||||||
|
bool isDir = attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
if (!isDir) {
|
||||||
|
mWatcher->mEvents.remove(mWatcher->mDir);
|
||||||
|
mTree->remove(mWatcher->mDir);
|
||||||
|
mWatcher->notify();
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if (errorCode != ERROR_SUCCESS) {
|
||||||
|
throw WatcherError("Unknown error", mWatcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap read and write buffers, and poll again
|
||||||
|
std::swap(mWriteBuffer, mReadBuffer);
|
||||||
|
poll();
|
||||||
|
|
||||||
|
// Read change events
|
||||||
|
BYTE *base = mReadBuffer.data();
|
||||||
|
while (true) {
|
||||||
|
PFILE_NOTIFY_INFORMATION info = (PFILE_NOTIFY_INFORMATION)base;
|
||||||
|
processEvent(info);
|
||||||
|
|
||||||
|
if (info->NextEntryOffset == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
base += info->NextEntryOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWatcher->notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processEvent(PFILE_NOTIFY_INFORMATION info) {
|
||||||
|
std::string path = mWatcher->mDir + "\\" + utf16ToUtf8(info->FileName, info->FileNameLength / sizeof(WCHAR));
|
||||||
|
if (mWatcher->isIgnored(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (info->Action) {
|
||||||
|
case FILE_ACTION_ADDED:
|
||||||
|
case FILE_ACTION_RENAMED_NEW_NAME: {
|
||||||
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||||
|
if (GetFileAttributesExW(utf8ToUtf16(path).data(), GetFileExInfoStandard, &data)) {
|
||||||
|
mWatcher->mEvents.create(path);
|
||||||
|
mTree->add(path, CONVERT_TIME(data.ftLastWriteTime), data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FILE_ACTION_MODIFIED: {
|
||||||
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||||
|
if (GetFileAttributesExW(utf8ToUtf16(path).data(), GetFileExInfoStandard, &data)) {
|
||||||
|
mTree->update(path, CONVERT_TIME(data.ftLastWriteTime));
|
||||||
|
if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
mWatcher->mEvents.update(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FILE_ACTION_REMOVED:
|
||||||
|
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||||
|
mWatcher->mEvents.remove(path);
|
||||||
|
mTree->remove(path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WindowsBackend *mBackend;
|
||||||
|
std::shared_ptr<Watcher> mWatcher;
|
||||||
|
std::shared_ptr<DirTree> mTree;
|
||||||
|
bool mRunning;
|
||||||
|
HANDLE mDirectoryHandle;
|
||||||
|
std::vector<BYTE> mReadBuffer;
|
||||||
|
std::vector<BYTE> mWriteBuffer;
|
||||||
|
OVERLAPPED mOverlapped;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This function is called by Backend::watch which takes a lock on mMutex
|
||||||
|
void WindowsBackend::subscribe(WatcherRef watcher) {
|
||||||
|
// Create a subscription for this watcher
|
||||||
|
auto sub = std::make_shared<Subscription>(this, watcher, getTree(watcher, false));
|
||||||
|
watcher->state = sub;
|
||||||
|
|
||||||
|
// Queue polling for this subscription in the correct thread.
|
||||||
|
bool success = QueueUserAPC([](__in ULONG_PTR ptr) {
|
||||||
|
Subscription *sub = (Subscription *)ptr;
|
||||||
|
sub->run();
|
||||||
|
}, mThread.native_handle(), (ULONG_PTR)sub.get());
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw std::runtime_error("Unable to queue APC");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called by Backend::unwatch which takes a lock on mMutex
|
||||||
|
void WindowsBackend::unsubscribe(WatcherRef watcher) {
|
||||||
|
watcher->state = nullptr;
|
||||||
|
}
|
||||||
18
node_modules/@parcel/watcher/src/windows/WindowsBackend.hh
generated
vendored
Normal file
18
node_modules/@parcel/watcher/src/windows/WindowsBackend.hh
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef WINDOWS_H
|
||||||
|
#define WINDOWS_H
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "../shared/BruteForceBackend.hh"
|
||||||
|
|
||||||
|
class WindowsBackend : public BruteForceBackend {
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
~WindowsBackend();
|
||||||
|
void subscribe(WatcherRef watcher) override;
|
||||||
|
void unsubscribe(WatcherRef watcher) override;
|
||||||
|
private:
|
||||||
|
bool mRunning;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
44
node_modules/@parcel/watcher/src/windows/win_utils.cc
generated
vendored
Normal file
44
node_modules/@parcel/watcher/src/windows/win_utils.cc
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "./win_utils.hh"
|
||||||
|
|
||||||
|
std::wstring utf8ToUtf16(std::string input) {
|
||||||
|
unsigned int len = MultiByteToWideChar(CP_UTF8, 0, input.c_str(), -1, NULL, 0);
|
||||||
|
WCHAR *output = new WCHAR[len];
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, input.c_str(), -1, output, len);
|
||||||
|
std::wstring res(output);
|
||||||
|
delete[] output;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string utf16ToUtf8(const WCHAR *input, DWORD length) {
|
||||||
|
unsigned int len = WideCharToMultiByte(CP_UTF8, 0, input, length, NULL, 0, NULL, NULL);
|
||||||
|
char *output = new char[len + 1];
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, input, length, output, len, NULL, NULL);
|
||||||
|
output[len] = '\0';
|
||||||
|
std::string res(output);
|
||||||
|
delete[] output;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalizePath(std::string path) {
|
||||||
|
// Prevent truncation to MAX_PATH characters by adding the \\?\ prefix
|
||||||
|
std::wstring p = utf8ToUtf16("\\\\?\\" + path);
|
||||||
|
|
||||||
|
// Get the required length for the output
|
||||||
|
DWORD len = GetLongPathNameW(p.data(), NULL, 0);
|
||||||
|
if (!len) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate output array and get long path
|
||||||
|
WCHAR *output = new WCHAR[len];
|
||||||
|
len = GetLongPathNameW(p.data(), output, len);
|
||||||
|
if (!len) {
|
||||||
|
delete[] output;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert back to utf8
|
||||||
|
std::string res = utf16ToUtf8(output + 4, len - 4);
|
||||||
|
delete[] output;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
11
node_modules/@parcel/watcher/src/windows/win_utils.hh
generated
vendored
Normal file
11
node_modules/@parcel/watcher/src/windows/win_utils.hh
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef WIN_UTILS_H
|
||||||
|
#define WIN_UTILS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
std::wstring utf8ToUtf16(std::string input);
|
||||||
|
std::string utf16ToUtf8(const WCHAR *input, DWORD length);
|
||||||
|
std::string normalizePath(std::string path);
|
||||||
|
|
||||||
|
#endif
|
||||||
74
node_modules/@parcel/watcher/wrapper.js
generated
vendored
Normal file
74
node_modules/@parcel/watcher/wrapper.js
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const picomatch = require('picomatch');
|
||||||
|
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 = picomatch.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,
|
||||||
|
windows: process.platform === 'win32',
|
||||||
|
});
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
20
node_modules/@popperjs/core/LICENSE.md
generated
vendored
Normal file
20
node_modules/@popperjs/core/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2019 Federico Zivolo
|
||||||
|
|
||||||
|
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.
|
||||||
376
node_modules/@popperjs/core/README.md
generated
vendored
Normal file
376
node_modules/@popperjs/core/README.md
generated
vendored
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
<!-- <HEADER> // IGNORE IT -->
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://rawcdn.githack.com/popperjs/popper-core/8805a5d7599e14619c9e7ac19a3713285d8e5d7f/docs/src/images/popper-logo-outlined.svg" alt="Popper" height="300px"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<h1>Tooltip & Popover Positioning Engine</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.npmjs.com/package/@popperjs/core">
|
||||||
|
<img src="https://img.shields.io/npm/v/@popperjs/core?style=for-the-badge" alt="npm version" />
|
||||||
|
</a>
|
||||||
|
<a href="https://www.npmjs.com/package/@popperjs/core">
|
||||||
|
<img src="https://img.shields.io/endpoint?style=for-the-badge&url=https://runkit.io/fezvrasta/combined-npm-downloads/1.0.0?packages=popper.js,@popperjs/core" alt="npm downloads per month (popper.js + @popperjs/core)" />
|
||||||
|
</a>
|
||||||
|
<a href="https://rollingversions.com/popperjs/popper-core">
|
||||||
|
<img src="https://img.shields.io/badge/Rolling%20Versions-Enabled-brightgreen?style=for-the-badge" alt="Rolling Versions" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<!-- </HEADER> // NOW BEGINS THE README -->
|
||||||
|
|
||||||
|
**Positioning tooltips and popovers is difficult. Popper is here to help!**
|
||||||
|
|
||||||
|
Given an element, such as a button, and a tooltip element describing it, Popper
|
||||||
|
will automatically put the tooltip in the right place near the button.
|
||||||
|
|
||||||
|
It will position _any_ UI element that "pops out" from the flow of your document
|
||||||
|
and floats near a target element. The most common example is a tooltip, but it
|
||||||
|
also includes popovers, drop-downs, and more. All of these can be generically
|
||||||
|
described as a "popper" element.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
[](https://popper.js.org)
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
- [v2.x (latest)](https://popper.js.org/docs/v2/)
|
||||||
|
- [v1.x](https://popper.js.org/docs/v1/)
|
||||||
|
|
||||||
|
We've created a
|
||||||
|
[Migration Guide](https://popper.js.org/docs/v2/migration-guide/) to help you
|
||||||
|
migrate from Popper 1 to Popper 2.
|
||||||
|
|
||||||
|
To contribute to the Popper website and documentation, please visit the
|
||||||
|
[dedicated repository](https://github.com/popperjs/website).
|
||||||
|
|
||||||
|
## Why not use pure CSS?
|
||||||
|
|
||||||
|
- **Clipping and overflow issues**: Pure CSS poppers will not be prevented from
|
||||||
|
overflowing clipping boundaries, such as the viewport. It will get partially
|
||||||
|
cut off or overflows if it's near the edge since there is no dynamic
|
||||||
|
positioning logic. When using Popper, your popper will always be positioned in
|
||||||
|
the right place without needing manual adjustments.
|
||||||
|
- **No flipping**: CSS poppers will not flip to a different placement to fit
|
||||||
|
better in view if necessary. While you can manually adjust for the main axis
|
||||||
|
overflow, this feature cannot be achieved via CSS alone. Popper automatically
|
||||||
|
flips the tooltip to make it fit in view as best as possible for the user.
|
||||||
|
- **No virtual positioning**: CSS poppers cannot follow the mouse cursor or be
|
||||||
|
used as a context menu. Popper allows you to position your tooltip relative to
|
||||||
|
any coordinates you desire.
|
||||||
|
- **Slower development cycle**: When pure CSS is used to position popper
|
||||||
|
elements, the lack of dynamic positioning means they must be carefully placed
|
||||||
|
to consider overflow on all screen sizes. In reusable component libraries,
|
||||||
|
this means a developer can't just add the component anywhere on the page,
|
||||||
|
because these issues need to be considered and adjusted for every time. With
|
||||||
|
Popper, you can place your elements anywhere and they will be positioned
|
||||||
|
correctly, without needing to consider different screen sizes, layouts, etc.
|
||||||
|
This massively speeds up development time because this work is automatically
|
||||||
|
offloaded to Popper.
|
||||||
|
- **Lack of extensibility**: CSS poppers cannot be easily extended to fit any
|
||||||
|
arbitrary use case you may need to adjust for. Popper is built with
|
||||||
|
extensibility in mind.
|
||||||
|
|
||||||
|
## Why Popper?
|
||||||
|
|
||||||
|
With the CSS drawbacks out of the way, we now move on to Popper in the
|
||||||
|
JavaScript space itself.
|
||||||
|
|
||||||
|
Naive JavaScript tooltip implementations usually have the following problems:
|
||||||
|
|
||||||
|
- **Scrolling containers**: They don't ensure the tooltip stays with the
|
||||||
|
reference element while scrolling when inside any number of scrolling
|
||||||
|
containers.
|
||||||
|
- **DOM context**: They often require the tooltip move outside of its original
|
||||||
|
DOM context because they don't handle `offsetParent` contexts.
|
||||||
|
- **Compatibility**: Popper handles an incredible number of edge cases regarding
|
||||||
|
different browsers and environments (mobile viewports, RTL, scrollbars enabled
|
||||||
|
or disabled, etc.). Popper is a popular and well-maintained library, so you
|
||||||
|
can be confident positioning will work for your users on any device.
|
||||||
|
- **Configurability**: They often lack advanced configurability to suit any
|
||||||
|
possible use case.
|
||||||
|
- **Size**: They are usually relatively large in size, or require an ancient
|
||||||
|
jQuery dependency.
|
||||||
|
- **Performance**: They often have runtime performance issues and update the
|
||||||
|
tooltip position too slowly.
|
||||||
|
|
||||||
|
**Popper solves all of these key problems in an elegant, performant manner.** It
|
||||||
|
is a lightweight ~3 kB library that aims to provide a reliable and extensible
|
||||||
|
positioning engine you can use to ensure all your popper elements are positioned
|
||||||
|
in the right place.
|
||||||
|
|
||||||
|
When you start writing your own popper implementation, you'll quickly run into
|
||||||
|
all of the problems mentioned above. These widgets are incredibly common in our
|
||||||
|
UIs; we've done the hard work figuring this out so you don't need to spend hours
|
||||||
|
fixing and handling numerous edge cases that we already ran into while building
|
||||||
|
the library!
|
||||||
|
|
||||||
|
Popper is used in popular libraries like Bootstrap, Foundation, Material UI, and
|
||||||
|
more. It's likely you've already used popper elements on the web positioned by
|
||||||
|
Popper at some point in the past few years.
|
||||||
|
|
||||||
|
Since we write UIs using powerful abstraction libraries such as React or Angular
|
||||||
|
nowadays, you'll also be glad to know Popper can fully integrate with them and
|
||||||
|
be a good citizen together with your other components. Check out `react-popper`
|
||||||
|
for the official Popper wrapper for React.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Package Manager
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# With npm
|
||||||
|
npm i @popperjs/core
|
||||||
|
|
||||||
|
# With Yarn
|
||||||
|
yarn add @popperjs/core
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. CDN
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Development version -->
|
||||||
|
<script src="https://unpkg.com/@popperjs/core@2/dist/umd/popper.js"></script>
|
||||||
|
|
||||||
|
<!-- Production version -->
|
||||||
|
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Direct Download?
|
||||||
|
|
||||||
|
Managing dependencies by "directly downloading" them and placing them into your
|
||||||
|
source code is not recommended for a variety of reasons, including missing out
|
||||||
|
on feat/fix updates easily. Please use a versioning management system like a CDN
|
||||||
|
or npm/Yarn.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The most straightforward way to get started is to import Popper from the `unpkg`
|
||||||
|
CDN, which includes all of its features. You can call the `Popper.createPopper`
|
||||||
|
constructor to create new popper instances.
|
||||||
|
|
||||||
|
Here is a complete example:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>Popper example</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#tooltip {
|
||||||
|
background-color: #333;
|
||||||
|
color: white;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<button id="button" aria-describedby="tooltip">I'm a button</button>
|
||||||
|
<div id="tooltip" role="tooltip">I'm a tooltip</div>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/@popperjs/core@^2.0.0"></script>
|
||||||
|
<script>
|
||||||
|
const button = document.querySelector('#button');
|
||||||
|
const tooltip = document.querySelector('#tooltip');
|
||||||
|
|
||||||
|
// Pass the button, the tooltip, and some options, and Popper will do the
|
||||||
|
// magic positioning for you:
|
||||||
|
Popper.createPopper(button, tooltip, {
|
||||||
|
placement: 'right',
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
Visit the [tutorial](https://popper.js.org/docs/v2/tutorial/) for an example of
|
||||||
|
how to build your own tooltip from scratch using Popper.
|
||||||
|
|
||||||
|
### Module bundlers
|
||||||
|
|
||||||
|
You can import the `createPopper` constructor from the fully-featured file:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createPopper } from '@popperjs/core';
|
||||||
|
|
||||||
|
const button = document.querySelector('#button');
|
||||||
|
const tooltip = document.querySelector('#tooltip');
|
||||||
|
|
||||||
|
// Pass the button, the tooltip, and some options, and Popper will do the
|
||||||
|
// magic positioning for you:
|
||||||
|
createPopper(button, tooltip, {
|
||||||
|
placement: 'right',
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
All the modifiers listed in the docs menu will be enabled and "just work", so
|
||||||
|
you don't need to think about setting Popper up. The size of Popper including
|
||||||
|
all of its features is about 5 kB minzipped, but it may grow a bit in the
|
||||||
|
future.
|
||||||
|
|
||||||
|
#### Popper Lite (tree-shaking)
|
||||||
|
|
||||||
|
If bundle size is important, you'll want to take advantage of tree-shaking. The
|
||||||
|
library is built in a modular way to allow to import only the parts you really
|
||||||
|
need.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createPopperLite as createPopper } from '@popperjs/core';
|
||||||
|
```
|
||||||
|
|
||||||
|
The Lite version includes the most necessary modifiers that will compute the
|
||||||
|
offsets of the popper, compute and add the positioning styles, and add event
|
||||||
|
listeners. This is close in bundle size to pure CSS tooltip libraries, and
|
||||||
|
behaves somewhat similarly.
|
||||||
|
|
||||||
|
However, this does not include the features that makes Popper truly useful.
|
||||||
|
|
||||||
|
The two most useful modifiers not included in Lite are `preventOverflow` and
|
||||||
|
`flip`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import {
|
||||||
|
createPopperLite as createPopper,
|
||||||
|
preventOverflow,
|
||||||
|
flip,
|
||||||
|
} from '@popperjs/core';
|
||||||
|
|
||||||
|
const button = document.querySelector('#button');
|
||||||
|
const tooltip = document.querySelector('#tooltip');
|
||||||
|
|
||||||
|
createPopper(button, tooltip, {
|
||||||
|
modifiers: [preventOverflow, flip],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
As you make more poppers, you may be finding yourself needing other modifiers
|
||||||
|
provided by the library.
|
||||||
|
|
||||||
|
See [tree-shaking](https://popper.js.org/docs/v2/performance/#tree-shaking) for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
## Distribution targets
|
||||||
|
|
||||||
|
Popper is distributed in 3 different versions, in 3 different file formats.
|
||||||
|
|
||||||
|
The 3 file formats are:
|
||||||
|
|
||||||
|
- `esm` (works with `import` syntax — **recommended**)
|
||||||
|
- `umd` (works with `<script>` tags or RequireJS)
|
||||||
|
- `cjs` (works with `require()` syntax)
|
||||||
|
|
||||||
|
There are two different `esm` builds, one for bundler consumers (e.g. webpack,
|
||||||
|
Rollup, etc..), which is located under `/lib`, and one for browsers with native
|
||||||
|
support for ES Modules, under `/dist/esm`. The only difference within the two,
|
||||||
|
is that the browser-compatible version doesn't make use of
|
||||||
|
`process.env.NODE_ENV` to run development checks.
|
||||||
|
|
||||||
|
The 3 versions are:
|
||||||
|
|
||||||
|
- `popper`: includes all the modifiers (features) in one file (**default**);
|
||||||
|
- `popper-lite`: includes only the minimum amount of modifiers to provide the
|
||||||
|
basic functionality;
|
||||||
|
- `popper-base`: doesn't include any modifier, you must import them separately;
|
||||||
|
|
||||||
|
Below you can find the size of each version, minified and compressed with the
|
||||||
|
[Brotli compression algorithm](https://medium.com/groww-engineering/enable-brotli-compression-in-webpack-with-fallback-to-gzip-397a57cf9fc6):
|
||||||
|
|
||||||
|
<!-- Don't change the labels to use hyphens, it breaks, even when encoded -->
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## Hacking the library
|
||||||
|
|
||||||
|
If you want to play with the library, implement new features, fix a bug you
|
||||||
|
found, or simply experiment with it, this section is for you!
|
||||||
|
|
||||||
|
First of all, make sure to have
|
||||||
|
[Yarn installed](https://yarnpkg.com/lang/en/docs/install).
|
||||||
|
|
||||||
|
Install the development dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
And run the development environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, simply open one the development server web page:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# macOS and Linux
|
||||||
|
open localhost:5000
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
start localhost:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
From there, you can open any of the examples (`.html` files) to fiddle with
|
||||||
|
them.
|
||||||
|
|
||||||
|
Now any change you will made to the source code, will be automatically compiled,
|
||||||
|
you just need to refresh the page.
|
||||||
|
|
||||||
|
If the page is not working properly, try to go in _"Developer Tools >
|
||||||
|
Application > Clear storage"_ and click on "_Clear site data_".
|
||||||
|
To run the examples you need a browser with
|
||||||
|
[JavaScript modules via script tag support](https://caniuse.com/#feat=es6-module).
|
||||||
|
|
||||||
|
## Test Suite
|
||||||
|
|
||||||
|
Popper is currently tested with unit tests, and functional tests. Both of them
|
||||||
|
are run by Jest.
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
The unit tests use JSDOM to provide a primitive document object API, they are
|
||||||
|
used to ensure the utility functions behave as expected in isolation.
|
||||||
|
|
||||||
|
### Functional Tests
|
||||||
|
|
||||||
|
The functional tests run with Puppeteer, to take advantage of a complete browser
|
||||||
|
environment. They are currently running on Chromium, and Firefox.
|
||||||
|
|
||||||
|
You can run them with `yarn test:functional`. Set the `PUPPETEER_BROWSER`
|
||||||
|
environment variable to `firefox` to run them on the Mozilla browser.
|
||||||
|
|
||||||
|
The assertions are written in form of image snapshots, so that it's easy to
|
||||||
|
assert for the correct Popper behavior without having to write a lot of offsets
|
||||||
|
comparisons manually.
|
||||||
|
|
||||||
|
You can mark a `*.test.js` file to run in the Puppeteer environment by
|
||||||
|
prepending a `@jest-environment puppeteer` JSDoc comment to the interested file.
|
||||||
|
|
||||||
|
Here's an example of a basic functional test:
|
||||||
|
|
||||||
|
```js
|
||||||
|
/**
|
||||||
|
* @jest-environment puppeteer
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
import { screenshot } from '../utils/puppeteer.js';
|
||||||
|
|
||||||
|
it('should position the popper on the right', async () => {
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto(`${TEST_URL}/basic.html`);
|
||||||
|
|
||||||
|
expect(await screenshot(page)).toMatchImageSnapshot();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
You can find the complete
|
||||||
|
[`jest-puppeteer` documentation here](https://github.com/smooth-code/jest-puppeteer#api),
|
||||||
|
and the
|
||||||
|
[`jest-image-snapshot` documentation here](https://github.com/americanexpress/jest-image-snapshot#%EF%B8%8F-api).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
65
node_modules/@popperjs/core/dist/cjs/enums.js
generated
vendored
Normal file
65
node_modules/@popperjs/core/dist/cjs/enums.js
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* @popperjs/core v2.11.8 - MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
|
||||||
|
var top = 'top';
|
||||||
|
var bottom = 'bottom';
|
||||||
|
var right = 'right';
|
||||||
|
var left = 'left';
|
||||||
|
var auto = 'auto';
|
||||||
|
var basePlacements = [top, bottom, right, left];
|
||||||
|
var start = 'start';
|
||||||
|
var end = 'end';
|
||||||
|
var clippingParents = 'clippingParents';
|
||||||
|
var viewport = 'viewport';
|
||||||
|
var popper = 'popper';
|
||||||
|
var reference = 'reference';
|
||||||
|
var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {
|
||||||
|
return acc.concat([placement + "-" + start, placement + "-" + end]);
|
||||||
|
}, []);
|
||||||
|
var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {
|
||||||
|
return acc.concat([placement, placement + "-" + start, placement + "-" + end]);
|
||||||
|
}, []); // modifiers that need to read the DOM
|
||||||
|
|
||||||
|
var beforeRead = 'beforeRead';
|
||||||
|
var read = 'read';
|
||||||
|
var afterRead = 'afterRead'; // pure-logic modifiers
|
||||||
|
|
||||||
|
var beforeMain = 'beforeMain';
|
||||||
|
var main = 'main';
|
||||||
|
var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)
|
||||||
|
|
||||||
|
var beforeWrite = 'beforeWrite';
|
||||||
|
var write = 'write';
|
||||||
|
var afterWrite = 'afterWrite';
|
||||||
|
var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];
|
||||||
|
|
||||||
|
exports.afterMain = afterMain;
|
||||||
|
exports.afterRead = afterRead;
|
||||||
|
exports.afterWrite = afterWrite;
|
||||||
|
exports.auto = auto;
|
||||||
|
exports.basePlacements = basePlacements;
|
||||||
|
exports.beforeMain = beforeMain;
|
||||||
|
exports.beforeRead = beforeRead;
|
||||||
|
exports.beforeWrite = beforeWrite;
|
||||||
|
exports.bottom = bottom;
|
||||||
|
exports.clippingParents = clippingParents;
|
||||||
|
exports.end = end;
|
||||||
|
exports.left = left;
|
||||||
|
exports.main = main;
|
||||||
|
exports.modifierPhases = modifierPhases;
|
||||||
|
exports.placements = placements;
|
||||||
|
exports.popper = popper;
|
||||||
|
exports.read = read;
|
||||||
|
exports.reference = reference;
|
||||||
|
exports.right = right;
|
||||||
|
exports.start = start;
|
||||||
|
exports.top = top;
|
||||||
|
exports.variationPlacements = variationPlacements;
|
||||||
|
exports.viewport = viewport;
|
||||||
|
exports.write = write;
|
||||||
|
//# sourceMappingURL=enums.js.map
|
||||||
3
node_modules/@popperjs/core/dist/cjs/enums.js.flow
generated
vendored
Normal file
3
node_modules/@popperjs/core/dist/cjs/enums.js.flow
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
export * from '../../lib/enums.js'
|
||||||
1
node_modules/@popperjs/core/dist/cjs/enums.js.map
generated
vendored
Normal file
1
node_modules/@popperjs/core/dist/cjs/enums.js.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"enums.js","sources":["../../src/enums.js"],"sourcesContent":["// @flow\nexport const top: 'top' = 'top';\nexport const bottom: 'bottom' = 'bottom';\nexport const right: 'right' = 'right';\nexport const left: 'left' = 'left';\nexport const auto: 'auto' = 'auto';\nexport type BasePlacement =\n | typeof top\n | typeof bottom\n | typeof right\n | typeof left;\nexport const basePlacements: Array<BasePlacement> = [top, bottom, right, left];\n\nexport const start: 'start' = 'start';\nexport const end: 'end' = 'end';\nexport type Variation = typeof start | typeof end;\n\nexport const clippingParents: 'clippingParents' = 'clippingParents';\nexport const viewport: 'viewport' = 'viewport';\nexport type Boundary = Element | Array<Element> | typeof clippingParents;\nexport type RootBoundary = typeof viewport | 'document';\n\nexport const popper: 'popper' = 'popper';\nexport const reference: 'reference' = 'reference';\nexport type Context = typeof popper | typeof reference;\n\nexport type VariationPlacement =\n | 'top-start'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-end'\n | 'right-start'\n | 'right-end'\n | 'left-start'\n | 'left-end';\nexport type AutoPlacement = 'auto' | 'auto-start' | 'auto-end';\nexport type ComputedPlacement = VariationPlacement | BasePlacement;\nexport type Placement = AutoPlacement | BasePlacement | VariationPlacement;\n\nexport const variationPlacements: Array<VariationPlacement> = basePlacements.reduce(\n (acc: Array<VariationPlacement>, placement: BasePlacement) =>\n acc.concat([(`${placement}-${start}`: any), (`${placement}-${end}`: any)]),\n []\n);\nexport const placements: Array<Placement> = [...basePlacements, auto].reduce(\n (\n acc: Array<Placement>,\n placement: BasePlacement | typeof auto\n ): Array<Placement> =>\n acc.concat([\n placement,\n (`${placement}-${start}`: any),\n (`${placement}-${end}`: any),\n ]),\n []\n);\n\n// modifiers that need to read the DOM\nexport const beforeRead: 'beforeRead' = 'beforeRead';\nexport const read: 'read' = 'read';\nexport const afterRead: 'afterRead' = 'afterRead';\n// pure-logic modifiers\nexport const beforeMain: 'beforeMain' = 'beforeMain';\nexport const main: 'main' = 'main';\nexport const afterMain: 'afterMain' = 'afterMain';\n// modifier with the purpose to write to the DOM (or write into a framework state)\nexport const beforeWrite: 'beforeWrite' = 'beforeWrite';\nexport const write: 'write' = 'write';\nexport const afterWrite: 'afterWrite' = 'afterWrite';\nexport const modifierPhases: Array<ModifierPhases> = [\n beforeRead,\n read,\n afterRead,\n beforeMain,\n main,\n afterMain,\n beforeWrite,\n write,\n afterWrite,\n];\n\nexport type ModifierPhases =\n | typeof beforeRead\n | typeof read\n | typeof afterRead\n | typeof beforeMain\n | typeof main\n | typeof afterMain\n | typeof beforeWrite\n | typeof write\n | typeof afterWrite;\n"],"names":["top","bottom","right","left","auto","basePlacements","start","end","clippingParents","viewport","popper","reference","variationPlacements","reduce","acc","placement","concat","placements","beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite","modifierPhases"],"mappings":";;;;;;;;IACaA,GAAU,GAAG;IACbC,MAAgB,GAAG;IACnBC,KAAc,GAAG;IACjBC,IAAY,GAAG;IACfC,IAAY,GAAG;IAMfC,cAAoC,GAAG,CAACL,GAAD,EAAMC,MAAN,EAAcC,KAAd,EAAqBC,IAArB;IAEvCG,KAAc,GAAG;IACjBC,GAAU,GAAG;IAGbC,eAAkC,GAAG;IACrCC,QAAoB,GAAG;IAIvBC,MAAgB,GAAG;IACnBC,SAAsB,GAAG;IAgBzBC,mBAA8C,gBAAGP,cAAc,CAACQ,MAAf,CAC5D,UAACC,GAAD,EAAiCC,SAAjC;AAAA,SACED,GAAG,CAACE,MAAJ,CAAW,CAAKD,SAAL,SAAkBT,KAAlB,EAAqCS,SAArC,SAAkDR,GAAlD,CAAX,CADF;AAAA,CAD4D,EAG5D,EAH4D;IAKjDU,UAA4B,gBAAG,UAAIZ,cAAJ,GAAoBD,IAApB,GAA0BS,MAA1B,CAC1C,UACEC,GADF,EAEEC,SAFF;AAAA,SAIED,GAAG,CAACE,MAAJ,CAAW,CACTD,SADS,EAELA,SAFK,SAEQT,KAFR,EAGLS,SAHK,SAGQR,GAHR,CAAX,CAJF;AAAA,CAD0C,EAU1C,EAV0C;;IAc/BW,UAAwB,GAAG;IAC3BC,IAAY,GAAG;IACfC,SAAsB,GAAG;;IAEzBC,UAAwB,GAAG;IAC3BC,IAAY,GAAG;IACfC,SAAsB,GAAG;;IAEzBC,WAA0B,GAAG;IAC7BC,KAAc,GAAG;IACjBC,UAAwB,GAAG;IAC3BC,cAAqC,GAAG,CACnDT,UADmD,EAEnDC,IAFmD,EAGnDC,SAHmD,EAInDC,UAJmD,EAKnDC,IALmD,EAMnDC,SANmD,EAOnDC,WAPmD,EAQnDC,KARmD,EASnDC,UATmD;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
||||||
939
node_modules/@popperjs/core/dist/cjs/popper-base.js
generated
vendored
Normal file
939
node_modules/@popperjs/core/dist/cjs/popper-base.js
generated
vendored
Normal file
@ -0,0 +1,939 @@
|
|||||||
|
/**
|
||||||
|
* @popperjs/core v2.11.8 - MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
|
||||||
|
function getWindow(node) {
|
||||||
|
if (node == null) {
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.toString() !== '[object Window]') {
|
||||||
|
var ownerDocument = node.ownerDocument;
|
||||||
|
return ownerDocument ? ownerDocument.defaultView || window : window;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isElement(node) {
|
||||||
|
var OwnElement = getWindow(node).Element;
|
||||||
|
return node instanceof OwnElement || node instanceof Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHTMLElement(node) {
|
||||||
|
var OwnElement = getWindow(node).HTMLElement;
|
||||||
|
return node instanceof OwnElement || node instanceof HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isShadowRoot(node) {
|
||||||
|
// IE 11 has no ShadowRoot
|
||||||
|
if (typeof ShadowRoot === 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var OwnElement = getWindow(node).ShadowRoot;
|
||||||
|
return node instanceof OwnElement || node instanceof ShadowRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
var max = Math.max;
|
||||||
|
var min = Math.min;
|
||||||
|
var round = Math.round;
|
||||||
|
|
||||||
|
function getUAString() {
|
||||||
|
var uaData = navigator.userAgentData;
|
||||||
|
|
||||||
|
if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {
|
||||||
|
return uaData.brands.map(function (item) {
|
||||||
|
return item.brand + "/" + item.version;
|
||||||
|
}).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return navigator.userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLayoutViewport() {
|
||||||
|
return !/^((?!chrome|android).)*safari/i.test(getUAString());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBoundingClientRect(element, includeScale, isFixedStrategy) {
|
||||||
|
if (includeScale === void 0) {
|
||||||
|
includeScale = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFixedStrategy === void 0) {
|
||||||
|
isFixedStrategy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var clientRect = element.getBoundingClientRect();
|
||||||
|
var scaleX = 1;
|
||||||
|
var scaleY = 1;
|
||||||
|
|
||||||
|
if (includeScale && isHTMLElement(element)) {
|
||||||
|
scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;
|
||||||
|
scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ref = isElement(element) ? getWindow(element) : window,
|
||||||
|
visualViewport = _ref.visualViewport;
|
||||||
|
|
||||||
|
var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;
|
||||||
|
var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;
|
||||||
|
var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;
|
||||||
|
var width = clientRect.width / scaleX;
|
||||||
|
var height = clientRect.height / scaleY;
|
||||||
|
return {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
top: y,
|
||||||
|
right: x + width,
|
||||||
|
bottom: y + height,
|
||||||
|
left: x,
|
||||||
|
x: x,
|
||||||
|
y: y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWindowScroll(node) {
|
||||||
|
var win = getWindow(node);
|
||||||
|
var scrollLeft = win.pageXOffset;
|
||||||
|
var scrollTop = win.pageYOffset;
|
||||||
|
return {
|
||||||
|
scrollLeft: scrollLeft,
|
||||||
|
scrollTop: scrollTop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHTMLElementScroll(element) {
|
||||||
|
return {
|
||||||
|
scrollLeft: element.scrollLeft,
|
||||||
|
scrollTop: element.scrollTop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNodeScroll(node) {
|
||||||
|
if (node === getWindow(node) || !isHTMLElement(node)) {
|
||||||
|
return getWindowScroll(node);
|
||||||
|
} else {
|
||||||
|
return getHTMLElementScroll(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNodeName(element) {
|
||||||
|
return element ? (element.nodeName || '').toLowerCase() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDocumentElement(element) {
|
||||||
|
// $FlowFixMe[incompatible-return]: assume body is always available
|
||||||
|
return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]
|
||||||
|
element.document) || window.document).documentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWindowScrollBarX(element) {
|
||||||
|
// If <html> has a CSS width greater than the viewport, then this will be
|
||||||
|
// incorrect for RTL.
|
||||||
|
// Popper 1 is broken in this case and never had a bug report so let's assume
|
||||||
|
// it's not an issue. I don't think anyone ever specifies width on <html>
|
||||||
|
// anyway.
|
||||||
|
// Browsers where the left scrollbar doesn't cause an issue report `0` for
|
||||||
|
// this (e.g. Edge 2019, IE11, Safari)
|
||||||
|
return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComputedStyle(element) {
|
||||||
|
return getWindow(element).getComputedStyle(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isScrollParent(element) {
|
||||||
|
// Firefox wants us to check `-x` and `-y` variations as well
|
||||||
|
var _getComputedStyle = getComputedStyle(element),
|
||||||
|
overflow = _getComputedStyle.overflow,
|
||||||
|
overflowX = _getComputedStyle.overflowX,
|
||||||
|
overflowY = _getComputedStyle.overflowY;
|
||||||
|
|
||||||
|
return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isElementScaled(element) {
|
||||||
|
var rect = element.getBoundingClientRect();
|
||||||
|
var scaleX = round(rect.width) / element.offsetWidth || 1;
|
||||||
|
var scaleY = round(rect.height) / element.offsetHeight || 1;
|
||||||
|
return scaleX !== 1 || scaleY !== 1;
|
||||||
|
} // Returns the composite rect of an element relative to its offsetParent.
|
||||||
|
// Composite means it takes into account transforms as well as layout.
|
||||||
|
|
||||||
|
|
||||||
|
function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {
|
||||||
|
if (isFixed === void 0) {
|
||||||
|
isFixed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isOffsetParentAnElement = isHTMLElement(offsetParent);
|
||||||
|
var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);
|
||||||
|
var documentElement = getDocumentElement(offsetParent);
|
||||||
|
var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);
|
||||||
|
var scroll = {
|
||||||
|
scrollLeft: 0,
|
||||||
|
scrollTop: 0
|
||||||
|
};
|
||||||
|
var offsets = {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
|
||||||
|
if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078
|
||||||
|
isScrollParent(documentElement)) {
|
||||||
|
scroll = getNodeScroll(offsetParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHTMLElement(offsetParent)) {
|
||||||
|
offsets = getBoundingClientRect(offsetParent, true);
|
||||||
|
offsets.x += offsetParent.clientLeft;
|
||||||
|
offsets.y += offsetParent.clientTop;
|
||||||
|
} else if (documentElement) {
|
||||||
|
offsets.x = getWindowScrollBarX(documentElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: rect.left + scroll.scrollLeft - offsets.x,
|
||||||
|
y: rect.top + scroll.scrollTop - offsets.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// means it doesn't take into account transforms.
|
||||||
|
|
||||||
|
function getLayoutRect(element) {
|
||||||
|
var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.
|
||||||
|
// Fixes https://github.com/popperjs/popper-core/issues/1223
|
||||||
|
|
||||||
|
var width = element.offsetWidth;
|
||||||
|
var height = element.offsetHeight;
|
||||||
|
|
||||||
|
if (Math.abs(clientRect.width - width) <= 1) {
|
||||||
|
width = clientRect.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.abs(clientRect.height - height) <= 1) {
|
||||||
|
height = clientRect.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: element.offsetLeft,
|
||||||
|
y: element.offsetTop,
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParentNode(element) {
|
||||||
|
if (getNodeName(element) === 'html') {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle
|
||||||
|
// $FlowFixMe[incompatible-return]
|
||||||
|
// $FlowFixMe[prop-missing]
|
||||||
|
element.assignedSlot || // step into the shadow DOM of the parent of a slotted node
|
||||||
|
element.parentNode || ( // DOM Element detected
|
||||||
|
isShadowRoot(element) ? element.host : null) || // ShadowRoot detected
|
||||||
|
// $FlowFixMe[incompatible-call]: HTMLElement is a Node
|
||||||
|
getDocumentElement(element) // fallback
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScrollParent(node) {
|
||||||
|
if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {
|
||||||
|
// $FlowFixMe[incompatible-return]: assume body is always available
|
||||||
|
return node.ownerDocument.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHTMLElement(node) && isScrollParent(node)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getScrollParent(getParentNode(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
given a DOM element, return the list of all scroll parents, up the list of ancesors
|
||||||
|
until we get to the top window object. This list is what we attach scroll listeners
|
||||||
|
to, because if any of these parent elements scroll, we'll need to re-calculate the
|
||||||
|
reference element's position.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function listScrollParents(element, list) {
|
||||||
|
var _element$ownerDocumen;
|
||||||
|
|
||||||
|
if (list === void 0) {
|
||||||
|
list = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var scrollParent = getScrollParent(element);
|
||||||
|
var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);
|
||||||
|
var win = getWindow(scrollParent);
|
||||||
|
var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;
|
||||||
|
var updatedList = list.concat(target);
|
||||||
|
return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here
|
||||||
|
updatedList.concat(listScrollParents(getParentNode(target)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTableElement(element) {
|
||||||
|
return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTrueOffsetParent(element) {
|
||||||
|
if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837
|
||||||
|
getComputedStyle(element).position === 'fixed') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return element.offsetParent;
|
||||||
|
} // `.offsetParent` reports `null` for fixed elements, while absolute elements
|
||||||
|
// return the containing block
|
||||||
|
|
||||||
|
|
||||||
|
function getContainingBlock(element) {
|
||||||
|
var isFirefox = /firefox/i.test(getUAString());
|
||||||
|
var isIE = /Trident/i.test(getUAString());
|
||||||
|
|
||||||
|
if (isIE && isHTMLElement(element)) {
|
||||||
|
// In IE 9, 10 and 11 fixed elements containing block is always established by the viewport
|
||||||
|
var elementCss = getComputedStyle(element);
|
||||||
|
|
||||||
|
if (elementCss.position === 'fixed') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentNode = getParentNode(element);
|
||||||
|
|
||||||
|
if (isShadowRoot(currentNode)) {
|
||||||
|
currentNode = currentNode.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {
|
||||||
|
var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that
|
||||||
|
// create a containing block.
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
|
||||||
|
|
||||||
|
if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {
|
||||||
|
return currentNode;
|
||||||
|
} else {
|
||||||
|
currentNode = currentNode.parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} // Gets the closest ancestor positioned element. Handles some edge cases,
|
||||||
|
// such as table ancestors and cross browser bugs.
|
||||||
|
|
||||||
|
|
||||||
|
function getOffsetParent(element) {
|
||||||
|
var window = getWindow(element);
|
||||||
|
var offsetParent = getTrueOffsetParent(element);
|
||||||
|
|
||||||
|
while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {
|
||||||
|
offsetParent = getTrueOffsetParent(offsetParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offsetParent || getContainingBlock(element) || window;
|
||||||
|
}
|
||||||
|
|
||||||
|
var top = 'top';
|
||||||
|
var bottom = 'bottom';
|
||||||
|
var right = 'right';
|
||||||
|
var left = 'left';
|
||||||
|
var basePlacements = [top, bottom, right, left];
|
||||||
|
var start = 'start';
|
||||||
|
var end = 'end';
|
||||||
|
var clippingParents = 'clippingParents';
|
||||||
|
var viewport = 'viewport';
|
||||||
|
var popper = 'popper';
|
||||||
|
var reference = 'reference';
|
||||||
|
|
||||||
|
var beforeRead = 'beforeRead';
|
||||||
|
var read = 'read';
|
||||||
|
var afterRead = 'afterRead'; // pure-logic modifiers
|
||||||
|
|
||||||
|
var beforeMain = 'beforeMain';
|
||||||
|
var main = 'main';
|
||||||
|
var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)
|
||||||
|
|
||||||
|
var beforeWrite = 'beforeWrite';
|
||||||
|
var write = 'write';
|
||||||
|
var afterWrite = 'afterWrite';
|
||||||
|
var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];
|
||||||
|
|
||||||
|
function order(modifiers) {
|
||||||
|
var map = new Map();
|
||||||
|
var visited = new Set();
|
||||||
|
var result = [];
|
||||||
|
modifiers.forEach(function (modifier) {
|
||||||
|
map.set(modifier.name, modifier);
|
||||||
|
}); // On visiting object, check for its dependencies and visit them recursively
|
||||||
|
|
||||||
|
function sort(modifier) {
|
||||||
|
visited.add(modifier.name);
|
||||||
|
var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);
|
||||||
|
requires.forEach(function (dep) {
|
||||||
|
if (!visited.has(dep)) {
|
||||||
|
var depModifier = map.get(dep);
|
||||||
|
|
||||||
|
if (depModifier) {
|
||||||
|
sort(depModifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result.push(modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiers.forEach(function (modifier) {
|
||||||
|
if (!visited.has(modifier.name)) {
|
||||||
|
// check for visited object
|
||||||
|
sort(modifier);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderModifiers(modifiers) {
|
||||||
|
// order based on dependencies
|
||||||
|
var orderedModifiers = order(modifiers); // order based on phase
|
||||||
|
|
||||||
|
return modifierPhases.reduce(function (acc, phase) {
|
||||||
|
return acc.concat(orderedModifiers.filter(function (modifier) {
|
||||||
|
return modifier.phase === phase;
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debounce(fn) {
|
||||||
|
var pending;
|
||||||
|
return function () {
|
||||||
|
if (!pending) {
|
||||||
|
pending = new Promise(function (resolve) {
|
||||||
|
Promise.resolve().then(function () {
|
||||||
|
pending = undefined;
|
||||||
|
resolve(fn());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return pending;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeByName(modifiers) {
|
||||||
|
var merged = modifiers.reduce(function (merged, current) {
|
||||||
|
var existing = merged[current.name];
|
||||||
|
merged[current.name] = existing ? Object.assign({}, existing, current, {
|
||||||
|
options: Object.assign({}, existing.options, current.options),
|
||||||
|
data: Object.assign({}, existing.data, current.data)
|
||||||
|
}) : current;
|
||||||
|
return merged;
|
||||||
|
}, {}); // IE11 does not support Object.values
|
||||||
|
|
||||||
|
return Object.keys(merged).map(function (key) {
|
||||||
|
return merged[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViewportRect(element, strategy) {
|
||||||
|
var win = getWindow(element);
|
||||||
|
var html = getDocumentElement(element);
|
||||||
|
var visualViewport = win.visualViewport;
|
||||||
|
var width = html.clientWidth;
|
||||||
|
var height = html.clientHeight;
|
||||||
|
var x = 0;
|
||||||
|
var y = 0;
|
||||||
|
|
||||||
|
if (visualViewport) {
|
||||||
|
width = visualViewport.width;
|
||||||
|
height = visualViewport.height;
|
||||||
|
var layoutViewport = isLayoutViewport();
|
||||||
|
|
||||||
|
if (layoutViewport || !layoutViewport && strategy === 'fixed') {
|
||||||
|
x = visualViewport.offsetLeft;
|
||||||
|
y = visualViewport.offsetTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
x: x + getWindowScrollBarX(element),
|
||||||
|
y: y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// of the `<html>` and `<body>` rect bounds if horizontally scrollable
|
||||||
|
|
||||||
|
function getDocumentRect(element) {
|
||||||
|
var _element$ownerDocumen;
|
||||||
|
|
||||||
|
var html = getDocumentElement(element);
|
||||||
|
var winScroll = getWindowScroll(element);
|
||||||
|
var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;
|
||||||
|
var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);
|
||||||
|
var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);
|
||||||
|
var x = -winScroll.scrollLeft + getWindowScrollBarX(element);
|
||||||
|
var y = -winScroll.scrollTop;
|
||||||
|
|
||||||
|
if (getComputedStyle(body || html).direction === 'rtl') {
|
||||||
|
x += max(html.clientWidth, body ? body.clientWidth : 0) - width;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
x: x,
|
||||||
|
y: y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function contains(parent, child) {
|
||||||
|
var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method
|
||||||
|
|
||||||
|
if (parent.contains(child)) {
|
||||||
|
return true;
|
||||||
|
} // then fallback to custom implementation with Shadow DOM support
|
||||||
|
else if (rootNode && isShadowRoot(rootNode)) {
|
||||||
|
var next = child;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (next && parent.isSameNode(next)) {
|
||||||
|
return true;
|
||||||
|
} // $FlowFixMe[prop-missing]: need a better way to handle this...
|
||||||
|
|
||||||
|
|
||||||
|
next = next.parentNode || next.host;
|
||||||
|
} while (next);
|
||||||
|
} // Give up, the result is false
|
||||||
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rectToClientRect(rect) {
|
||||||
|
return Object.assign({}, rect, {
|
||||||
|
left: rect.x,
|
||||||
|
top: rect.y,
|
||||||
|
right: rect.x + rect.width,
|
||||||
|
bottom: rect.y + rect.height
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInnerBoundingClientRect(element, strategy) {
|
||||||
|
var rect = getBoundingClientRect(element, false, strategy === 'fixed');
|
||||||
|
rect.top = rect.top + element.clientTop;
|
||||||
|
rect.left = rect.left + element.clientLeft;
|
||||||
|
rect.bottom = rect.top + element.clientHeight;
|
||||||
|
rect.right = rect.left + element.clientWidth;
|
||||||
|
rect.width = element.clientWidth;
|
||||||
|
rect.height = element.clientHeight;
|
||||||
|
rect.x = rect.left;
|
||||||
|
rect.y = rect.top;
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClientRectFromMixedType(element, clippingParent, strategy) {
|
||||||
|
return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));
|
||||||
|
} // A "clipping parent" is an overflowable container with the characteristic of
|
||||||
|
// clipping (or hiding) overflowing elements with a position different from
|
||||||
|
// `initial`
|
||||||
|
|
||||||
|
|
||||||
|
function getClippingParents(element) {
|
||||||
|
var clippingParents = listScrollParents(getParentNode(element));
|
||||||
|
var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;
|
||||||
|
var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;
|
||||||
|
|
||||||
|
if (!isElement(clipperElement)) {
|
||||||
|
return [];
|
||||||
|
} // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414
|
||||||
|
|
||||||
|
|
||||||
|
return clippingParents.filter(function (clippingParent) {
|
||||||
|
return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';
|
||||||
|
});
|
||||||
|
} // Gets the maximum area that the element is visible in due to any number of
|
||||||
|
// clipping parents
|
||||||
|
|
||||||
|
|
||||||
|
function getClippingRect(element, boundary, rootBoundary, strategy) {
|
||||||
|
var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);
|
||||||
|
var clippingParents = [].concat(mainClippingParents, [rootBoundary]);
|
||||||
|
var firstClippingParent = clippingParents[0];
|
||||||
|
var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {
|
||||||
|
var rect = getClientRectFromMixedType(element, clippingParent, strategy);
|
||||||
|
accRect.top = max(rect.top, accRect.top);
|
||||||
|
accRect.right = min(rect.right, accRect.right);
|
||||||
|
accRect.bottom = min(rect.bottom, accRect.bottom);
|
||||||
|
accRect.left = max(rect.left, accRect.left);
|
||||||
|
return accRect;
|
||||||
|
}, getClientRectFromMixedType(element, firstClippingParent, strategy));
|
||||||
|
clippingRect.width = clippingRect.right - clippingRect.left;
|
||||||
|
clippingRect.height = clippingRect.bottom - clippingRect.top;
|
||||||
|
clippingRect.x = clippingRect.left;
|
||||||
|
clippingRect.y = clippingRect.top;
|
||||||
|
return clippingRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBasePlacement(placement) {
|
||||||
|
return placement.split('-')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVariation(placement) {
|
||||||
|
return placement.split('-')[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMainAxisFromPlacement(placement) {
|
||||||
|
return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeOffsets(_ref) {
|
||||||
|
var reference = _ref.reference,
|
||||||
|
element = _ref.element,
|
||||||
|
placement = _ref.placement;
|
||||||
|
var basePlacement = placement ? getBasePlacement(placement) : null;
|
||||||
|
var variation = placement ? getVariation(placement) : null;
|
||||||
|
var commonX = reference.x + reference.width / 2 - element.width / 2;
|
||||||
|
var commonY = reference.y + reference.height / 2 - element.height / 2;
|
||||||
|
var offsets;
|
||||||
|
|
||||||
|
switch (basePlacement) {
|
||||||
|
case top:
|
||||||
|
offsets = {
|
||||||
|
x: commonX,
|
||||||
|
y: reference.y - element.height
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bottom:
|
||||||
|
offsets = {
|
||||||
|
x: commonX,
|
||||||
|
y: reference.y + reference.height
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case right:
|
||||||
|
offsets = {
|
||||||
|
x: reference.x + reference.width,
|
||||||
|
y: commonY
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case left:
|
||||||
|
offsets = {
|
||||||
|
x: reference.x - element.width,
|
||||||
|
y: commonY
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
offsets = {
|
||||||
|
x: reference.x,
|
||||||
|
y: reference.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;
|
||||||
|
|
||||||
|
if (mainAxis != null) {
|
||||||
|
var len = mainAxis === 'y' ? 'height' : 'width';
|
||||||
|
|
||||||
|
switch (variation) {
|
||||||
|
case start:
|
||||||
|
offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case end:
|
||||||
|
offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return offsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFreshSideObject() {
|
||||||
|
return {
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergePaddingObject(paddingObject) {
|
||||||
|
return Object.assign({}, getFreshSideObject(), paddingObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandToHashMap(value, keys) {
|
||||||
|
return keys.reduce(function (hashMap, key) {
|
||||||
|
hashMap[key] = value;
|
||||||
|
return hashMap;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectOverflow(state, options) {
|
||||||
|
if (options === void 0) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var _options = options,
|
||||||
|
_options$placement = _options.placement,
|
||||||
|
placement = _options$placement === void 0 ? state.placement : _options$placement,
|
||||||
|
_options$strategy = _options.strategy,
|
||||||
|
strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,
|
||||||
|
_options$boundary = _options.boundary,
|
||||||
|
boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,
|
||||||
|
_options$rootBoundary = _options.rootBoundary,
|
||||||
|
rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,
|
||||||
|
_options$elementConte = _options.elementContext,
|
||||||
|
elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,
|
||||||
|
_options$altBoundary = _options.altBoundary,
|
||||||
|
altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,
|
||||||
|
_options$padding = _options.padding,
|
||||||
|
padding = _options$padding === void 0 ? 0 : _options$padding;
|
||||||
|
var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));
|
||||||
|
var altContext = elementContext === popper ? reference : popper;
|
||||||
|
var popperRect = state.rects.popper;
|
||||||
|
var element = state.elements[altBoundary ? altContext : elementContext];
|
||||||
|
var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);
|
||||||
|
var referenceClientRect = getBoundingClientRect(state.elements.reference);
|
||||||
|
var popperOffsets = computeOffsets({
|
||||||
|
reference: referenceClientRect,
|
||||||
|
element: popperRect,
|
||||||
|
strategy: 'absolute',
|
||||||
|
placement: placement
|
||||||
|
});
|
||||||
|
var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));
|
||||||
|
var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect
|
||||||
|
// 0 or negative = within the clipping rect
|
||||||
|
|
||||||
|
var overflowOffsets = {
|
||||||
|
top: clippingClientRect.top - elementClientRect.top + paddingObject.top,
|
||||||
|
bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,
|
||||||
|
left: clippingClientRect.left - elementClientRect.left + paddingObject.left,
|
||||||
|
right: elementClientRect.right - clippingClientRect.right + paddingObject.right
|
||||||
|
};
|
||||||
|
var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element
|
||||||
|
|
||||||
|
if (elementContext === popper && offsetData) {
|
||||||
|
var offset = offsetData[placement];
|
||||||
|
Object.keys(overflowOffsets).forEach(function (key) {
|
||||||
|
var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;
|
||||||
|
var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';
|
||||||
|
overflowOffsets[key] += offset[axis] * multiply;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return overflowOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
var DEFAULT_OPTIONS = {
|
||||||
|
placement: 'bottom',
|
||||||
|
modifiers: [],
|
||||||
|
strategy: 'absolute'
|
||||||
|
};
|
||||||
|
|
||||||
|
function areValidElements() {
|
||||||
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||||
|
args[_key] = arguments[_key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return !args.some(function (element) {
|
||||||
|
return !(element && typeof element.getBoundingClientRect === 'function');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function popperGenerator(generatorOptions) {
|
||||||
|
if (generatorOptions === void 0) {
|
||||||
|
generatorOptions = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var _generatorOptions = generatorOptions,
|
||||||
|
_generatorOptions$def = _generatorOptions.defaultModifiers,
|
||||||
|
defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,
|
||||||
|
_generatorOptions$def2 = _generatorOptions.defaultOptions,
|
||||||
|
defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;
|
||||||
|
return function createPopper(reference, popper, options) {
|
||||||
|
if (options === void 0) {
|
||||||
|
options = defaultOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = {
|
||||||
|
placement: 'bottom',
|
||||||
|
orderedModifiers: [],
|
||||||
|
options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),
|
||||||
|
modifiersData: {},
|
||||||
|
elements: {
|
||||||
|
reference: reference,
|
||||||
|
popper: popper
|
||||||
|
},
|
||||||
|
attributes: {},
|
||||||
|
styles: {}
|
||||||
|
};
|
||||||
|
var effectCleanupFns = [];
|
||||||
|
var isDestroyed = false;
|
||||||
|
var instance = {
|
||||||
|
state: state,
|
||||||
|
setOptions: function setOptions(setOptionsAction) {
|
||||||
|
var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;
|
||||||
|
cleanupModifierEffects();
|
||||||
|
state.options = Object.assign({}, defaultOptions, state.options, options);
|
||||||
|
state.scrollParents = {
|
||||||
|
reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],
|
||||||
|
popper: listScrollParents(popper)
|
||||||
|
}; // Orders the modifiers based on their dependencies and `phase`
|
||||||
|
// properties
|
||||||
|
|
||||||
|
var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers
|
||||||
|
|
||||||
|
state.orderedModifiers = orderedModifiers.filter(function (m) {
|
||||||
|
return m.enabled;
|
||||||
|
});
|
||||||
|
runModifierEffects();
|
||||||
|
return instance.update();
|
||||||
|
},
|
||||||
|
// Sync update – it will always be executed, even if not necessary. This
|
||||||
|
// is useful for low frequency updates where sync behavior simplifies the
|
||||||
|
// logic.
|
||||||
|
// For high frequency updates (e.g. `resize` and `scroll` events), always
|
||||||
|
// prefer the async Popper#update method
|
||||||
|
forceUpdate: function forceUpdate() {
|
||||||
|
if (isDestroyed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var _state$elements = state.elements,
|
||||||
|
reference = _state$elements.reference,
|
||||||
|
popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements
|
||||||
|
// anymore
|
||||||
|
|
||||||
|
if (!areValidElements(reference, popper)) {
|
||||||
|
return;
|
||||||
|
} // Store the reference and popper rects to be read by modifiers
|
||||||
|
|
||||||
|
|
||||||
|
state.rects = {
|
||||||
|
reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),
|
||||||
|
popper: getLayoutRect(popper)
|
||||||
|
}; // Modifiers have the ability to reset the current update cycle. The
|
||||||
|
// most common use case for this is the `flip` modifier changing the
|
||||||
|
// placement, which then needs to re-run all the modifiers, because the
|
||||||
|
// logic was previously ran for the previous placement and is therefore
|
||||||
|
// stale/incorrect
|
||||||
|
|
||||||
|
state.reset = false;
|
||||||
|
state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier
|
||||||
|
// is filled with the initial data specified by the modifier. This means
|
||||||
|
// it doesn't persist and is fresh on each update.
|
||||||
|
// To ensure persistent data, use `${name}#persistent`
|
||||||
|
|
||||||
|
state.orderedModifiers.forEach(function (modifier) {
|
||||||
|
return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var index = 0; index < state.orderedModifiers.length; index++) {
|
||||||
|
if (state.reset === true) {
|
||||||
|
state.reset = false;
|
||||||
|
index = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var _state$orderedModifie = state.orderedModifiers[index],
|
||||||
|
fn = _state$orderedModifie.fn,
|
||||||
|
_state$orderedModifie2 = _state$orderedModifie.options,
|
||||||
|
_options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,
|
||||||
|
name = _state$orderedModifie.name;
|
||||||
|
|
||||||
|
if (typeof fn === 'function') {
|
||||||
|
state = fn({
|
||||||
|
state: state,
|
||||||
|
options: _options,
|
||||||
|
name: name,
|
||||||
|
instance: instance
|
||||||
|
}) || state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Async and optimistically optimized update – it will not be executed if
|
||||||
|
// not necessary (debounced to run at most once-per-tick)
|
||||||
|
update: debounce(function () {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
instance.forceUpdate();
|
||||||
|
resolve(state);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
destroy: function destroy() {
|
||||||
|
cleanupModifierEffects();
|
||||||
|
isDestroyed = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!areValidElements(reference, popper)) {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.setOptions(options).then(function (state) {
|
||||||
|
if (!isDestroyed && options.onFirstUpdate) {
|
||||||
|
options.onFirstUpdate(state);
|
||||||
|
}
|
||||||
|
}); // Modifiers have the ability to execute arbitrary code before the first
|
||||||
|
// update cycle runs. They will be executed in the same order as the update
|
||||||
|
// cycle. This is useful when a modifier adds some persistent data that
|
||||||
|
// other modifiers need to use, but the modifier is run after the dependent
|
||||||
|
// one.
|
||||||
|
|
||||||
|
function runModifierEffects() {
|
||||||
|
state.orderedModifiers.forEach(function (_ref) {
|
||||||
|
var name = _ref.name,
|
||||||
|
_ref$options = _ref.options,
|
||||||
|
options = _ref$options === void 0 ? {} : _ref$options,
|
||||||
|
effect = _ref.effect;
|
||||||
|
|
||||||
|
if (typeof effect === 'function') {
|
||||||
|
var cleanupFn = effect({
|
||||||
|
state: state,
|
||||||
|
name: name,
|
||||||
|
instance: instance,
|
||||||
|
options: options
|
||||||
|
});
|
||||||
|
|
||||||
|
var noopFn = function noopFn() {};
|
||||||
|
|
||||||
|
effectCleanupFns.push(cleanupFn || noopFn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupModifierEffects() {
|
||||||
|
effectCleanupFns.forEach(function (fn) {
|
||||||
|
return fn();
|
||||||
|
});
|
||||||
|
effectCleanupFns = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules
|
||||||
|
|
||||||
|
exports.createPopper = createPopper;
|
||||||
|
exports.detectOverflow = detectOverflow;
|
||||||
|
exports.popperGenerator = popperGenerator;
|
||||||
|
//# sourceMappingURL=popper-base.js.map
|
||||||
3
node_modules/@popperjs/core/dist/cjs/popper-base.js.flow
generated
vendored
Normal file
3
node_modules/@popperjs/core/dist/cjs/popper-base.js.flow
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
export * from '../../lib/popper-base.js'
|
||||||
1
node_modules/@popperjs/core/dist/cjs/popper-base.js.map
generated
vendored
Normal file
1
node_modules/@popperjs/core/dist/cjs/popper-base.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1260
node_modules/@popperjs/core/dist/cjs/popper-lite.js
generated
vendored
Normal file
1260
node_modules/@popperjs/core/dist/cjs/popper-lite.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3
node_modules/@popperjs/core/dist/cjs/popper-lite.js.flow
generated
vendored
Normal file
3
node_modules/@popperjs/core/dist/cjs/popper-lite.js.flow
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
export * from '../../lib/popper-lite.js'
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user