Add <%br= %> jqhtml syntax docs, class override detection, npm update

Document event handler placement and model fetch clarification

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2026-01-15 10:16:06 +00:00
parent 61f8f058f2
commit 1594502cb2
791 changed files with 7044 additions and 6089 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@parcel/watcher-linux-x64-glibc",
"version": "2.5.1",
"version": "2.5.4",
"main": "watcher.node",
"repository": {
"type": "git",

Binary file not shown.

View File

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

View File

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

View File

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

Binary file not shown.

View File

@@ -103,7 +103,7 @@ You can specify the exact backend you wish to use by passing the `backend` optio
All of the APIs in `@parcel/watcher` support the following options, which are passed as an object as the last function argument.
- `ignore` - an array of paths or glob patterns to ignore. uses [`is-glob`](https://github.com/micromatch/is-glob) to distinguish paths from globs. glob patterns are parsed with [`micromatch`](https://github.com/micromatch/micromatch) (see [features](https://github.com/micromatch/micromatch#matching-features)).
- `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.
@@ -129,6 +129,7 @@ subscribe(/* ... */);
- [Gatsby Cloud](https://twitter.com/chatsidhartha/status/1435647412828196867)
- [Nx](https://nx.dev)
- [Nuxt](https://nuxt.com)
- [Meteor](https://github.com/meteor/meteor)
## License

View File

@@ -65,7 +65,21 @@
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1, # /EHsc
"AdditionalOptions": ['-std:c++17']
"AdditionalOptions": [
"-std:c++17",
"/guard:cf",
"/W3",
"/w34244",
"/w34267",
"/sdl",
"/ZH:SHA_256"
]
},
"VCLinkerTool": {
"AdditionalOptions": [
"/DYNAMICBASE",
"/guard:cf"
]
}
}
}],

View File

@@ -2,7 +2,8 @@ const {createWrapper} = require('./wrapper');
let name = `@parcel/watcher-${process.platform}-${process.arch}`;
if (process.platform === 'linux') {
const { MUSL, family } = require('detect-libc');
const { MUSL, familySync } = require('detect-libc');
const family = familySync();
if (family === MUSL) {
name += '-musl';
} else {

View File

@@ -1,6 +1,6 @@
{
"name": "@parcel/watcher",
"version": "2.5.1",
"version": "2.5.4",
"main": "index.js",
"types": "index.d.ts",
"repository": {
@@ -50,10 +50,10 @@
]
},
"dependencies": {
"detect-libc": "^1.0.3",
"detect-libc": "^2.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
"node-addon-api": "^7.0.0",
"picomatch": "^4.0.3"
},
"devDependencies": {
"esbuild": "^0.19.8",
@@ -71,18 +71,18 @@
]
},
"optionalDependencies": {
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1"
"@parcel/watcher-darwin-x64": "2.5.4",
"@parcel/watcher-darwin-arm64": "2.5.4",
"@parcel/watcher-win32-x64": "2.5.4",
"@parcel/watcher-win32-arm64": "2.5.4",
"@parcel/watcher-win32-ia32": "2.5.4",
"@parcel/watcher-linux-x64-glibc": "2.5.4",
"@parcel/watcher-linux-x64-musl": "2.5.4",
"@parcel/watcher-linux-arm64-glibc": "2.5.4",
"@parcel/watcher-linux-arm64-musl": "2.5.4",
"@parcel/watcher-linux-arm-glibc": "2.5.4",
"@parcel/watcher-linux-arm-musl": "2.5.4",
"@parcel/watcher-android-arm64": "2.5.4",
"@parcel/watcher-freebsd-x64": "2.5.4"
}
}

View File

@@ -21,7 +21,11 @@
#include "Backend.hh"
#include <unordered_map>
static std::unordered_map<std::string, std::shared_ptr<Backend>> sharedBackends;
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.
@@ -65,8 +69,8 @@ std::shared_ptr<Backend> getBackend(std::string backend) {
}
std::shared_ptr<Backend> Backend::getShared(std::string backend) {
auto found = sharedBackends.find(backend);
if (found != sharedBackends.end()) {
auto found = getSharedBackends().find(backend);
if (found != getSharedBackends().end()) {
return found->second;
}
@@ -76,21 +80,21 @@ std::shared_ptr<Backend> Backend::getShared(std::string backend) {
}
result->run();
sharedBackends.emplace(backend, result);
getSharedBackends().emplace(backend, result);
return result;
}
void removeShared(Backend *backend) {
for (auto it = sharedBackends.begin(); it != sharedBackends.end(); it++) {
for (auto it = getSharedBackends().begin(); it != getSharedBackends().end(); it++) {
if (it->second.get() == backend) {
sharedBackends.erase(it);
getSharedBackends().erase(it);
break;
}
}
// Free up memory.
if (sharedBackends.size() == 0) {
sharedBackends.rehash(0);
if (getSharedBackends().size() == 0) {
getSharedBackends().rehash(0);
}
}
@@ -145,7 +149,7 @@ void Backend::watch(WatcherRef watcher) {
try {
this->subscribe(watcher);
mSubscriptions.insert(watcher);
} catch (std::exception &err) {
} catch (std::exception&) {
unref();
throw;
}

View File

@@ -1,34 +1,46 @@
#include "DirTree.hh"
#include <inttypes.h>
static std::mutex mDirCacheMutex;
static std::unordered_map<std::string, std::weak_ptr<DirTree>> dirTreeCache;
// "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);
dirTreeCache.erase(tree->root);
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 (dirTreeCache.size() == 0) {
dirTreeCache.rehash(0);
if (cache.size() == 0) {
cache.rehash(0);
}
}
};
std::shared_ptr<DirTree> DirTree::getCached(std::string root) {
std::lock_guard<std::mutex> lock(mDirCacheMutex);
std::lock_guard<std::mutex> lock(mDirCacheMutex());
std::unordered_map<std::string, std::weak_ptr<DirTree>> &cache = dirTreeCache();
auto found = dirTreeCache.find(root);
auto found = cache.find(root);
std::shared_ptr<DirTree> tree;
// Use cached tree, or create an empty one.
if (found != dirTreeCache.end()) {
if (found != cache.end()) {
tree = found->second.lock();
} else {
tree = std::shared_ptr<DirTree>(new DirTree(root), DirTreeDeleter());
dirTreeCache.emplace(root, tree);
cache.emplace(root, tree);
}
return tree;
@@ -55,7 +67,7 @@ DirEntry *DirTree::_find(std::string path) {
}
DirEntry *DirTree::add(std::string path, uint64_t mtime, bool isDir) {
std::lock_guard<std::mutex> lock(mMutex);
std::lock_guard<std::mutex> lock(mDirCacheMutex());
DirEntry entry(path, mtime, isDir);
auto it = entries.emplace(entry.path, entry);
@@ -63,12 +75,12 @@ DirEntry *DirTree::add(std::string path, uint64_t mtime, bool isDir) {
}
DirEntry *DirTree::find(std::string path) {
std::lock_guard<std::mutex> lock(mMutex);
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(mMutex);
std::lock_guard<std::mutex> lock(mDirCacheMutex());
DirEntry *found = _find(path);
if (found) {
@@ -79,7 +91,7 @@ DirEntry *DirTree::update(std::string path, uint64_t mtime) {
}
void DirTree::remove(std::string path) {
std::lock_guard<std::mutex> lock(mMutex);
std::lock_guard<std::mutex> lock(mDirCacheMutex());
DirEntry *found = _find(path);
@@ -99,7 +111,7 @@ void DirTree::remove(std::string path) {
}
void DirTree::write(FILE *f) {
std::lock_guard<std::mutex> lock(mMutex);
std::lock_guard<std::mutex> lock(mDirCacheMutex());
fprintf(f, "%zu\n", entries.size());
for (auto it = entries.begin(); it != entries.end(); it++) {
@@ -108,7 +120,7 @@ void DirTree::write(FILE *f) {
}
void DirTree::getChanges(DirTree *snapshot, EventList &events) {
std::lock_guard<std::mutex> lock(mMutex);
std::lock_guard<std::mutex> lock(mDirCacheMutex());
std::lock_guard<std::mutex> snapshotLock(snapshot->mMutex);
for (auto it = entries.begin(); it != entries.end(); it++) {

View File

@@ -14,7 +14,7 @@ struct Glob {
Glob(std::string raw);
bool operator==(const Glob &other) const {
return mHash == other.mHash;
return mHash == other.mHash && mRaw == other.mRaw;
}
bool isIgnored(std::string relative_path) const;

View File

@@ -15,30 +15,34 @@ struct WatcherCompare {
}
};
static std::unordered_set<WatcherRef , WatcherHash, WatcherCompare> sharedWatchers;
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 = sharedWatchers.find(watcher);
if (found != sharedWatchers.end()) {
auto found = getSharedWatchers().find(watcher);
if (found != getSharedWatchers().end()) {
return *found;
}
sharedWatchers.insert(watcher);
getSharedWatchers().insert(watcher);
return watcher;
}
void removeShared(Watcher *watcher) {
for (auto it = sharedWatchers.begin(); it != sharedWatchers.end(); it++) {
for (auto it = getSharedWatchers().begin(); it != getSharedWatchers().end(); it++) {
if (it->get() == watcher) {
sharedWatchers.erase(it);
getSharedWatchers().erase(it);
break;
}
}
// Free up memory.
if (sharedWatchers.size() == 0) {
sharedWatchers.rehash(0);
if (getSharedWatchers().size() == 0) {
getSharedWatchers().rehash(0);
}
}
@@ -84,7 +88,7 @@ struct CallbackData {
Value callbackEventsToJS(const Env &env, std::vector<Event> &events) {
EscapableHandleScope scope(env);
Array arr = Array::New(env, events.size());
size_t currentEventIndex = 0;
uint32_t currentEventIndex = 0;
for (auto eventIterator = events.begin(); eventIterator != events.end(); eventIterator++) {
arr.Set(currentEventIndex++, eventIterator->toJS(env));
}

View File

@@ -18,7 +18,7 @@ std::unordered_set<std::string> getIgnorePaths(Env env, Value opts) {
if (v.IsArray()) {
Array items = v.As<Array>();
for (size_t i = 0; i < items.Length(); i++) {
Value item = items.Get(Number::New(env, 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()));
}
@@ -37,7 +37,7 @@ std::unordered_set<Glob> getIgnoreGlobs(Env env, Value opts) {
if (v.IsArray()) {
Array items = v.As<Array>();
for (size_t i = 0; i < items.Length(); i++) {
Value item = items.Get(Number::New(env, i));
Value item = items.Get(Number::New(env, static_cast<double>(i)));
if (item.IsString()) {
auto key = item.As<String>().Utf8Value();
try {
@@ -124,7 +124,7 @@ private:
Value getResult() override {
std::vector<Event> events = watcher->mEvents.getEvents();
Array eventsArray = Array::New(env, events.size());
size_t i = 0;
uint32_t i = 0;
for (auto it = events.begin(); it != events.end(); it++) {
eventsArray.Set(i++, it->toJS(env));
}
@@ -183,7 +183,7 @@ private:
void execute() override {
try {
backend->watch(watcher);
} catch (std::exception &err) {
} catch (std::exception&) {
watcher->destroy();
throw;
}

View File

@@ -170,7 +170,9 @@ bool InotifyBackend::handleSubscription(struct inotify_event *event, std::shared
struct stat st;
// Use lstat to avoid resolving symbolic links that we cannot watch anyway
// https://github.com/parcel-bundler/watcher/issues/76
lstat(path.c_str(), &st);
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) {
@@ -184,7 +186,9 @@ bool InotifyBackend::handleSubscription(struct inotify_event *event, std::shared
watcher->mEvents.update(path);
struct stat st;
stat(path.c_str(), &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));

View File

@@ -98,7 +98,7 @@ public:
value.push_back(BSER(iss));
}
}
BSER::Array arrayValue() override {
return value;
}
@@ -184,7 +184,7 @@ public:
BSERBoolean(bool value) : Value(value) {}
bool boolValue() override { return value; }
void encode(std::ostream &oss) override {
int8_t t = value == true ? BSER_BOOL_TRUE : BSER_BOOL_FALSE;
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));
}
};
@@ -295,7 +295,7 @@ std::string BSER::encode() {
std::ostringstream res(std::ios_base::binary);
res.write("\x00\x01", 2);
BSERInteger(oss.str().size()).encode(res);
res << oss.str();
return res.str();

View File

@@ -77,7 +77,7 @@ public:
bool success = WriteFile(
mPipe, // pipe handle
buf.data(), // message
buf.size(), // message length
static_cast<DWORD>(buf.size()), // message length
NULL, // bytes written
&overlapped // overlapped
);
@@ -125,7 +125,7 @@ public:
bool success = ReadFile(
mPipe, // pipe handle
buf, // buffer to receive reply
len, // size of buffer
static_cast<DWORD>(len), // size of buffer
NULL, // number of bytes read
&overlapped // overlapped
);

View File

@@ -21,7 +21,7 @@ template<typename T>
BSER readBSER(T &&do_read) {
std::stringstream oss;
char buffer[256];
int r;
size_t r;
int64_t len = -1;
do {
// Start by reading a minimal amount of data in order to decode the length.
@@ -46,7 +46,11 @@ std::string getSockPath() {
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");
}
@@ -104,7 +108,7 @@ bool WatchmanBackend::checkAvailable() {
try {
watchmanConnect();
return true;
} catch (std::exception &err) {
} catch (std::exception&) {
return false;
}
}

View File

@@ -5,17 +5,17 @@ std::wstring utf8ToUtf16(std::string input) {
WCHAR *output = new WCHAR[len];
MultiByteToWideChar(CP_UTF8, 0, input.c_str(), -1, output, len);
std::wstring res(output);
delete output;
delete[] output;
return res;
}
std::string utf16ToUtf8(const WCHAR *input, size_t length) {
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;
delete[] output;
return res;
}
@@ -24,7 +24,7 @@ std::string normalizePath(std::string path) {
std::wstring p = utf8ToUtf16("\\\\?\\" + path);
// Get the required length for the output
unsigned int len = GetLongPathNameW(p.data(), NULL, 0);
DWORD len = GetLongPathNameW(p.data(), NULL, 0);
if (!len) {
return path;
}
@@ -33,12 +33,12 @@ std::string normalizePath(std::string path) {
WCHAR *output = new WCHAR[len];
len = GetLongPathNameW(p.data(), output, len);
if (!len) {
delete output;
delete[] output;
return path;
}
// Convert back to utf8
std::string res = utf16ToUtf8(output + 4, len - 4);
delete output;
delete[] output;
return res;
}

View File

@@ -5,7 +5,7 @@
#include <windows.h>
std::wstring utf8ToUtf16(std::string input);
std::string utf16ToUtf8(const WCHAR *input, size_t length);
std::string utf16ToUtf8(const WCHAR *input, DWORD length);
std::string normalizePath(std::string path);
#endif

View File

@@ -1,5 +1,5 @@
const path = require('path');
const micromatch = require('micromatch');
const picomatch = require('picomatch');
const isGlob = require('is-glob');
function normalizeOptions(dir, opts = {}) {
@@ -14,16 +14,13 @@ function normalizeOptions(dir, opts = {}) {
opts.ignoreGlobs = [];
}
const regex = micromatch.makeRe(value, {
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,
// C++ does not support lookbehind regex patterns, they
// were only added later to JavaScript engines
// (https://bit.ly/3V7S6UL)
lookbehinds: false
windows: process.platform === 'win32',
});
opts.ignoreGlobs.push(regex.source);
} else {