Initial commit: notification-elements-demo app

Interactive Angular 19 demo for @sda/notification-elements-ui with
6 sections: Bell & Feed, Notification Center, Inbox, Comments &
Threads, Mention Input, and Full-Featured layout. Includes mock
data, dark mode toggle, and real-time event log.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Giuliano Silvestro
2026-02-13 21:49:19 +10:00
commit 5d0c9ec7eb
36473 changed files with 3778146 additions and 0 deletions

20
node_modules/webpack-dev-server/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
Copyright JS Foundation and other contributors
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.

326
node_modules/webpack-dev-server/README.md generated vendored Normal file
View File

@@ -0,0 +1,326 @@
<div align="center">
<a href="https://github.com/webpack/webpack">
<img width="200" height="200" src="https://webpack.js.org/assets/icon-square-big.svg">
</a>
</div>
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![tests][tests]][tests-url]
[![coverage][cover]][cover-url]
[![discussion][discussion]][discussion-url]
[![downloads][downloads]][npm-url]
[![contributors][contributors]][contributors-url]
# webpack-dev-server
Use [webpack](https://webpack.js.org) with a development server that provides
live reloading. This should be used for **development only**.
It uses [webpack-dev-middleware][middleware-url] under the hood, which provides
fast in-memory access to the webpack assets.
## Table of Contents
- [Getting Started](#getting-started)
- [Usage](#usage)
- [With the CLI](#with-the-cli)
- [With NPM Scripts](#with-npm-scripts)
- [With the API](#with-the-api)
- [With TypeScript](#with-typescript)
- [The Result](#the-result)
- [Browser Support](#browser-support)
- [Support](#support)
- [Contributing](#contributing)
- [Attribution](#attribution)
- [License](#license)
## Getting Started
First things first, install the module:
```console
npm install webpack-dev-server --save-dev
```
or
```console
yarn add -D webpack-dev-server
```
or
```console
pnpm add -D webpack-dev-server
```
_Note: While you can install and run webpack-dev-server globally, we recommend
installing it locally. webpack-dev-server will always use a local installation
over a global one._
## Usage
There are two main, recommended methods of using the module:
### With the CLI
The easiest way to use it is with the [webpack CLI](https://webpack.js.org/api/cli/). In the directory where your
`webpack.config.js` is, run:
```console
npx webpack serve
```
Following options are available with `webpack serve`:
```
Usage: webpack serve|server|s [entries...] [options]
Run the webpack dev server.
Options:
-c, --config <value...> Provide path to a webpack configuration file e.g. ./webpack.config.js.
--config-name <value...> Name of the configuration to use.
-m, --merge Merge two or more configurations using 'webpack-merge'.
--disable-interpret Disable interpret for loading the config file.
--env <value...> Environment passed to the configuration when it is a function.
--node-env <value> Sets process.env.NODE_ENV to the specified value.
--define-process-env-node-env <value> Sets process.env.NODE_ENV to the specified value. (Currently an alias for `--node-env`)
--analyze It invokes webpack-bundle-analyzer plugin to get bundle information.
--progress [value] Print compilation progress during build.
-j, --json [value] Prints result as JSON or store it in a file.
--fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack
-d, --devtool <value> A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map).
--no-devtool Negative 'devtool' option.
--entry <value...> A module that is loaded upon startup. Only the last one is exported.
--mode <value> Enable production optimizations or development hints.
--name <value> Name of the configuration. Used when loading multiple configurations.
-o, --output-path <value> The output directory as **absolute path** (required).
--stats [value] Stats options object or preset name.
--no-stats Negative 'stats' option.
-t, --target <value...> Environment to build for. Environment to build for. An array of environments to build for all of them when possible.
--no-target Negative 'target' option.
--watch-options-stdin Stop watching when stdin stream has ended.
--no-watch-options-stdin Negative 'watch-options-stdin' option.
--allowed-hosts <value...> Allows to enumerate the hosts from which access to the dev server are allowed (useful when you are proxying dev server, by default is 'auto').
--allowed-hosts-reset Clear all items provided in 'allowedHosts' configuration. Allows to enumerate the hosts from which access to the dev server are allowed (useful when you are proxying dev server, by default is 'auto').
--bonjour Allows to broadcasts dev server via ZeroConf networking on start.
--no-bonjour Disallows to broadcasts dev server via ZeroConf networking on start.
--no-client Disables client script.
--client-logging <value> Allows to set log level in the browser.
--client-overlay Enables a full-screen overlay in the browser when there are compiler errors or warnings.
--no-client-overlay Disables the full-screen overlay in the browser when there are compiler errors or warnings.
--client-overlay-errors Enables a full-screen overlay in the browser when there are compiler errors.
--no-client-overlay-errors Disables the full-screen overlay in the browser when there are compiler errors.
--client-overlay-warnings Enables a full-screen overlay in the browser when there are compiler warnings.
--no-client-overlay-warnings Disables the full-screen overlay in the browser when there are compiler warnings.
--client-overlay-runtime-errors Enables a full-screen overlay in the browser when there are uncaught runtime errors.
--no-client-overlay-runtime-errors Disables the full-screen overlay in the browser when there are uncaught runtime errors.
--client-overlay-trusted-types-policy-name <value> The name of a Trusted Types policy for the overlay. Defaults to 'webpack-dev-server#overlay'.
--client-progress Prints compilation progress in percentage in the browser.
--no-client-progress Does not print compilation progress in percentage in the browser.
--client-reconnect [value] Tells dev-server the number of times it should try to reconnect the client.
--no-client-reconnect Tells dev-server to not to try to reconnect the client.
--client-web-socket-transport <value> Allows to set custom web socket transport to communicate with dev server.
--client-web-socket-url <value> Allows to specify URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to).
--client-web-socket-url-hostname <value> Tells clients connected to devServer to use the provided hostname.
--client-web-socket-url-pathname <value> Tells clients connected to devServer to use the provided path to connect.
--client-web-socket-url-password <value> Tells clients connected to devServer to use the provided password to authenticate.
--client-web-socket-url-port <value> Tells clients connected to devServer to use the provided port.
--client-web-socket-url-protocol <value> Tells clients connected to devServer to use the provided protocol.
--client-web-socket-url-username <value> Tells clients connected to devServer to use the provided username to authenticate.
--compress Enables gzip compression for everything served.
--no-compress Disables gzip compression for everything served.
--history-api-fallback Allows to proxy requests through a specified index page (by default 'index.html'), useful for Single Page Applications that utilise the HTML5 History API.
--no-history-api-fallback Disallows to proxy requests through a specified index page.
--host <value> Allows to specify a hostname to use.
--hot [value] Enables Hot Module Replacement.
--no-hot Disables Hot Module Replacement.
--ipc [value] Listen to a unix socket.
--live-reload Enables reload/refresh the page(s) when file changes are detected (enabled by default).
--no-live-reload Disables reload/refresh the page(s) when file changes are detected (enabled by default).
--open [value...] Allows to configure dev server to open the browser(s) and page(s) after server had been started (set it to true to open your default browser).
--no-open Does not open the default browser.
--open-target <value...> Opens specified page in browser.
--open-app-name <value...> Open specified browser.
--open-reset Clear all items provided in 'open' configuration. Allows to configure dev server to open the browser(s) and page(s) after server had been started (set it to true to open your default browser).
--open-target-reset Clear all items provided in 'open.target' configuration. Opens specified page in browser.
--open-app-name-reset Clear all items provided in 'open.app.name' configuration. Open specified browser.
--port <value> Allows to specify a port to use.
--server-type <value> Allows to set server and options (by default 'http').
--server-options-passphrase <value> Passphrase for a pfx file.
--server-options-request-cert Request for an SSL certificate.
--no-server-options-request-cert Does not request for an SSL certificate.
--server-options-ca <value...> Path to an SSL CA certificate or content of an SSL CA certificate.
--server-options-ca-reset Clear all items provided in 'server.options.ca' configuration. Path to an SSL CA certificate or content of an SSL CA certificate.
--server-options-cert <value...> Path to an SSL certificate or content of an SSL certificate.
--server-options-cert-reset Clear all items provided in 'server.options.cert' configuration. Path to an SSL certificate or content of an SSL certificate.
--server-options-crl <value...> Path to PEM formatted CRLs (Certificate Revocation Lists) or content of PEM formatted CRLs (Certificate Revocation Lists).
--server-options-crl-reset Clear all items provided in 'server.options.crl' configuration. Path to PEM formatted CRLs (Certificate Revocation Lists) or content of PEM formatted CRLs (Certificate Revocation Lists).
--server-options-key <value...> Path to an SSL key or content of an SSL key.
--server-options-key-reset Clear all items provided in 'server.options.key' configuration. Path to an SSL key or content of an SSL key.
--server-options-pfx <value...> Path to an SSL pfx file or content of an SSL pfx file.
--server-options-pfx-reset Clear all items provided in 'server.options.pfx' configuration. Path to an SSL pfx file or content of an SSL pfx file.
--static [value...] Allows to configure options for serving static files from directory (by default 'public' directory).
--no-static Disallows to configure options for serving static files from directory.
--static-directory <value...> Directory for static contents.
--static-public-path <value...> The static files will be available in the browser under this public path.
--static-serve-index Tells dev server to use serveIndex middleware when enabled.
--no-static-serve-index Does not tell dev server to use serveIndex middleware.
--static-watch Watches for files in static content directory.
--no-static-watch Does not watch for files in static content directory.
--static-reset Clear all items provided in 'static' configuration. Allows to configure options for serving static files from directory (by default 'public' directory).
--static-public-path-reset Clear all items provided in 'static.publicPath' configuration. The static files will be available in the browser under this public path.
--watch-files <value...> Allows to configure list of globs/directories/files to watch for file changes.
--watch-files-reset Clear all items provided in 'watchFiles' configuration. Allows to configure list of globs/directories/files to watch for file changes.
--no-web-socket-server Disallows to set web socket server and options.
--web-socket-server-type <value> Allows to set web socket server and options (by default 'ws').
Global options:
--color Enable colors on console.
--no-color Disable colors on console.
-v, --version Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands.
-h, --help [verbose] Display help for commands and options.
To see list of all supported commands and options run 'webpack --help=verbose'.
Webpack documentation: https://webpack.js.org/.
CLI documentation: https://webpack.js.org/api/cli/.
Made with ♥ by the webpack team.
```
> [!NOTE]
>
> _Detailed documentation for above options is available on this [link](https://webpack.js.org/configuration/dev-server/)._
### With NPM Scripts
NPM package.json scripts are a convenient and useful means to run locally installed
binaries without having to be concerned about their full paths. Simply define a
script as such:
```json
{
"scripts": {
"serve": "webpack serve"
}
}
```
And run the following in your terminal/console:
```console
npm run serve
```
NPM will automatically refer to the the binary in `node_modules` for you, and
execute the file or command.
### With the API
While it's recommended to run webpack-dev-server via the CLI, you may also choose to start a server via the API.
See the related [API documentation for `webpack-dev-server`](https://webpack.js.org/api/webpack-dev-server/).
### With TypeScript
If you use TypeScript in the webpack config, you'll need to properly type `devServer` property in order to avoid TS errors (e.g. `'devServer' does not exist in type 'Configuration'`). For that use either:
```ts
/// <reference path="node_modules/webpack-dev-server/types/lib/Server.d.ts"/>
import type { Configuration } from "webpack";
// Your logic
```
Or you can import the type from `webpack-dev-server`, i.e.
```ts
import type { Configuration as DevServerConfiguration } from "webpack-dev-server";
import type { Configuration } from "webpack";
const devServer: DevServerConfiguration = {};
const config: Configuration = { devServer };
// module.exports
export default config;
```
### The Result
Either method will start a server instance and begin listening for connections
from `localhost` on port `8080`.
webpack-dev-server is configured by default to support live-reload of files as
you edit your assets while the server is running.
See [**the documentation**][docs-url] for more use cases and options.
## Browser Support
While `webpack-dev-server` transpiles the client (browser) scripts to an ES5
state, the project only officially supports the _last two versions of major
browsers_. We simply don't have the resources to support every whacky
browser out there.
If you find a bug with an obscure / old browser, we would actively welcome a
Pull Request to resolve the bug.
## Support
We do our best to keep issues in the repository focused on bugs, features, and
needed modifications to the code for the module. Because of that, we ask users
with general support, "how-to", or "why isn't this working" questions to try one
of the other support channels that are available.
Your first-stop-shop for support for webpack-dev-server should be the excellent
[documentation][docs-url] for the module. If you see an opportunity for improvement
of those docs, please head over to the [webpack.js.org repo][wjo-url] and open a
pull request.
From there, we encourage users to visit the [webpack discussions][discussion-url] and
talk to the fine folks there. If your quest for answers comes up dry in chat,
head over to [StackOverflow][stack-url] and do a quick search or open a new
question. Remember; It's always much easier to answer questions that include your
`webpack.config.js` and relevant files!
If you're twitter-savvy you can tweet [#webpack][hash-url] with your question
and someone should be able to reach out and lend a hand.
If you have discovered a :bug:, have a feature suggestion, or would like to see
a modification, please feel free to create an issue on Github. _Note: The issue
template isn't optional, so please be sure not to remove it, and please fill it
out completely._
## Contributing
We welcome your contributions! Please have a read of [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to get involved.
## Attribution
This project is heavily inspired by [peerigon/nof5](https://github.com/peerigon/nof5).
## License
#### [MIT](./LICENSE)
[npm]: https://img.shields.io/npm/v/webpack-dev-server.svg
[npm-url]: https://npmjs.com/package/webpack-dev-server
[node]: https://img.shields.io/node/v/webpack-dev-server.svg
[node-url]: https://nodejs.org
[tests]: https://github.com/webpack/webpack-dev-server/workflows/webpack-dev-server/badge.svg
[tests-url]: https://github.com/webpack/webpack-dev-server/actions?query=workflow%3Awebpack-dev-server
[cover]: https://codecov.io/gh/webpack/webpack-dev-server/branch/master/graph/badge.svg
[cover-url]: https://codecov.io/gh/webpack/webpack-dev-server
[discussion]: https://img.shields.io/github/discussions/webpack/webpack
[discussion-url]: https://github.com/webpack/webpack/discussions
[docs-url]: https://webpack.js.org/configuration/dev-server/#devserver
[hash-url]: https://twitter.com/search?q=webpack
[middleware-url]: https://github.com/webpack/webpack-dev-middleware
[stack-url]: https://stackoverflow.com/questions/tagged/webpack-dev-server
[uglify-url]: https://github.com/webpack-contrib/uglifyjs-webpack-plugin
[wjo-url]: https://github.com/webpack/webpack.js.org
[downloads]: https://img.shields.io/npm/dm/webpack-dev-server.svg
[contributors-url]: https://github.com/webpack/webpack-dev-server/graphs/contributors
[contributors]: https://img.shields.io/github/contributors/webpack/webpack-dev-server.svg

1294
node_modules/webpack-dev-server/bin/cli-flags.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

199
node_modules/webpack-dev-server/bin/webpack-dev-server.js generated vendored Executable file
View File

@@ -0,0 +1,199 @@
#!/usr/bin/env node
/* Based on webpack/bin/webpack.js */
/* eslint-disable no-console */
"use strict";
/**
* @param {string} command process to run
* @param {string[]} args command line arguments
* @returns {Promise<void>} promise
*/
const runCommand = (command, args) => {
const cp = require("child_process");
return new Promise((resolve, reject) => {
const executedCommand = cp.spawn(command, args, {
stdio: "inherit",
shell: true,
});
executedCommand.on("error", (error) => {
reject(error);
});
executedCommand.on("exit", (code) => {
if (code === 0) {
resolve();
} else {
reject();
}
});
});
};
/**
* @param {string} packageName name of the package
* @returns {boolean} is the package installed?
*/
const isInstalled = (packageName) => {
if (process.versions.pnp) {
return true;
}
const path = require("path");
const fs = require("graceful-fs");
let dir = __dirname;
do {
try {
if (
fs.statSync(path.join(dir, "node_modules", packageName)).isDirectory()
) {
return true;
}
} catch (_error) {
// Nothing
}
// eslint-disable-next-line no-cond-assign
} while (dir !== (dir = path.dirname(dir)));
// https://github.com/nodejs/node/blob/v18.9.1/lib/internal/modules/cjs/loader.js#L1274
// @ts-ignore
for (const internalPath of require("module").globalPaths) {
try {
if (fs.statSync(path.join(internalPath, packageName)).isDirectory()) {
return true;
}
} catch (_error) {
// Nothing
}
}
return false;
};
/**
* @param {CliOption} cli options
* @returns {void}
*/
const runCli = (cli) => {
if (cli.preprocess) {
cli.preprocess();
}
const path = require("path");
const pkgPath = require.resolve(`${cli.package}/package.json`);
// eslint-disable-next-line import/no-dynamic-require
const pkg = require(pkgPath);
if (pkg.type === "module" || /\.mjs/i.test(pkg.bin[cli.binName])) {
import(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName])).catch(
(error) => {
console.error(error);
process.exitCode = 1;
},
);
} else {
// eslint-disable-next-line import/no-dynamic-require
require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]));
}
};
/**
* @typedef {Object} CliOption
* @property {string} name display name
* @property {string} package npm package name
* @property {string} binName name of the executable file
* @property {boolean} installed currently installed?
* @property {string} url homepage
* @property {function} preprocess preprocessor
*/
/** @type {CliOption} */
const cli = {
name: "webpack-cli",
package: "webpack-cli",
binName: "webpack-cli",
installed: isInstalled("webpack-cli"),
url: "https://github.com/webpack/webpack-cli",
preprocess() {
process.argv.splice(2, 0, "serve");
},
};
if (!cli.installed) {
const path = require("path");
const fs = require("graceful-fs");
const readLine = require("readline");
const notify = `CLI for webpack must be installed.\n ${cli.name} (${cli.url})\n`;
console.error(notify);
/**
* @type {string}
*/
let packageManager;
if (fs.existsSync(path.resolve(process.cwd(), "yarn.lock"))) {
packageManager = "yarn";
} else if (fs.existsSync(path.resolve(process.cwd(), "pnpm-lock.yaml"))) {
packageManager = "pnpm";
} else {
packageManager = "npm";
}
const installOptions = [packageManager === "yarn" ? "add" : "install", "-D"];
console.error(
`We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
" ",
)} ${cli.package}".`,
);
const question = `Do you want to install 'webpack-cli' (yes/no): `;
const questionInterface = readLine.createInterface({
input: process.stdin,
output: process.stderr,
});
// In certain scenarios (e.g. when STDIN is not in terminal mode), the callback function will not be
// executed. Setting the exit code here to ensure the script exits correctly in those cases. The callback
// function is responsible for clearing the exit code if the user wishes to install webpack-cli.
process.exitCode = 1;
questionInterface.question(question, (answer) => {
questionInterface.close();
const normalizedAnswer = answer.toLowerCase().startsWith("y");
if (!normalizedAnswer) {
console.error(
"You need to install 'webpack-cli' to use webpack via CLI.\n" +
"You can also install the CLI manually.",
);
return;
}
process.exitCode = 0;
console.log(
`Installing '${
cli.package
}' (running '${packageManager} ${installOptions.join(" ")} ${
cli.package
}')...`,
);
runCommand(packageManager, installOptions.concat(cli.package))
.then(() => {
runCli(cli);
})
.catch((error) => {
console.error(error);
process.exitCode = 1;
});
});
} else {
runCli(cli);
}

View File

@@ -0,0 +1,61 @@
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import SockJS from "../modules/sockjs-client/index.js";
import { log } from "../utils/log.js";
var SockJSClient = /*#__PURE__*/function () {
/**
* @param {string} url
*/
function SockJSClient(url) {
_classCallCheck(this, SockJSClient);
// SockJS requires `http` and `https` protocols
this.sock = new SockJS(url.replace(/^ws:/i, "http:").replace(/^wss:/i, "https:"));
this.sock.onerror =
/**
* @param {Error} error
*/
function (error) {
log.error(error);
};
}
/**
* @param {(...args: any[]) => void} f
*/
return _createClass(SockJSClient, [{
key: "onOpen",
value: function onOpen(f) {
this.sock.onopen = f;
}
/**
* @param {(...args: any[]) => void} f
*/
}, {
key: "onClose",
value: function onClose(f) {
this.sock.onclose = f;
}
// call f with the message string as the first argument
/**
* @param {(...args: any[]) => void} f
*/
}, {
key: "onMessage",
value: function onMessage(f) {
this.sock.onmessage =
/**
* @param {Error & { data: string }} e
*/
function (e) {
f(e.data);
};
}
}]);
}();
export { SockJSClient as default };

View File

@@ -0,0 +1,51 @@
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import { log } from "../utils/log.js";
var WebSocketClient = /*#__PURE__*/function () {
/**
* @param {string} url
*/
function WebSocketClient(url) {
_classCallCheck(this, WebSocketClient);
this.client = new WebSocket(url);
this.client.onerror = function (error) {
log.error(error);
};
}
/**
* @param {(...args: any[]) => void} f
*/
return _createClass(WebSocketClient, [{
key: "onOpen",
value: function onOpen(f) {
this.client.onopen = f;
}
/**
* @param {(...args: any[]) => void} f
*/
}, {
key: "onClose",
value: function onClose(f) {
this.client.onclose = f;
}
// call f with the message string as the first argument
/**
* @param {(...args: any[]) => void} f
*/
}, {
key: "onMessage",
value: function onMessage(f) {
this.client.onmessage = function (e) {
f(e.data);
};
}
}]);
}();
export { WebSocketClient as default };

591
node_modules/webpack-dev-server/client/index.js generated vendored Normal file
View File

@@ -0,0 +1,591 @@
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
/* global __resourceQuery, __webpack_hash__ */
/// <reference types="webpack/module" />
import webpackHotLog from "webpack/hot/log.js";
import hotEmitter from "webpack/hot/emitter.js";
import socket from "./socket.js";
import { formatProblem, createOverlay } from "./overlay.js";
import { log, setLogLevel } from "./utils/log.js";
import sendMessage from "./utils/sendMessage.js";
import { isProgressSupported, defineProgressElement } from "./progress.js";
/**
* @typedef {Object} OverlayOptions
* @property {boolean | (error: Error) => boolean} [warnings]
* @property {boolean | (error: Error) => boolean} [errors]
* @property {boolean | (error: Error) => boolean} [runtimeErrors]
* @property {string} [trustedTypesPolicyName]
*/
/**
* @typedef {Object} Options
* @property {boolean} hot
* @property {boolean} liveReload
* @property {boolean} progress
* @property {boolean | OverlayOptions} overlay
* @property {string} [logging]
* @property {number} [reconnect]
*/
/**
* @typedef {Object} Status
* @property {boolean} isUnloading
* @property {string} currentHash
* @property {string} [previousHash]
*/
/**
* @param {boolean | { warnings?: boolean | string; errors?: boolean | string; runtimeErrors?: boolean | string; }} overlayOptions
*/
var decodeOverlayOptions = function decodeOverlayOptions(overlayOptions) {
if (_typeof(overlayOptions) === "object") {
["warnings", "errors", "runtimeErrors"].forEach(function (property) {
if (typeof overlayOptions[property] === "string") {
var overlayFilterFunctionString = decodeURIComponent(overlayOptions[property]);
// eslint-disable-next-line no-new-func
overlayOptions[property] = new Function("message", "var callback = ".concat(overlayFilterFunctionString, "\n return callback(message)"));
}
});
}
};
/**
* @type {Status}
*/
var status = {
isUnloading: false,
// eslint-disable-next-line camelcase
currentHash: __webpack_hash__
};
/**
* @returns {string}
*/
var getCurrentScriptSource = function getCurrentScriptSource() {
// `document.currentScript` is the most accurate way to find the current script,
// but is not supported in all browsers.
if (document.currentScript) {
return document.currentScript.getAttribute("src");
}
// Fallback to getting all scripts running in the document.
var scriptElements = document.scripts || [];
var scriptElementsWithSrc = Array.prototype.filter.call(scriptElements, function (element) {
return element.getAttribute("src");
});
if (scriptElementsWithSrc.length > 0) {
var currentScript = scriptElementsWithSrc[scriptElementsWithSrc.length - 1];
return currentScript.getAttribute("src");
}
// Fail as there was no script to use.
throw new Error("[webpack-dev-server] Failed to get current script source.");
};
/**
* @param {string} resourceQuery
* @returns {{ [key: string]: string | boolean }}
*/
var parseURL = function parseURL(resourceQuery) {
/** @type {{ [key: string]: string }} */
var result = {};
if (typeof resourceQuery === "string" && resourceQuery !== "") {
var searchParams = resourceQuery.slice(1).split("&");
for (var i = 0; i < searchParams.length; i++) {
var pair = searchParams[i].split("=");
result[pair[0]] = decodeURIComponent(pair[1]);
}
} else {
// Else, get the url from the <script> this file was called with.
var scriptSource = getCurrentScriptSource();
var scriptSourceURL;
try {
// The placeholder `baseURL` with `window.location.href`,
// is to allow parsing of path-relative or protocol-relative URLs,
// and will have no effect if `scriptSource` is a fully valid URL.
scriptSourceURL = new URL(scriptSource, self.location.href);
} catch (error) {
// URL parsing failed, do nothing.
// We will still proceed to see if we can recover using `resourceQuery`
}
if (scriptSourceURL) {
result = scriptSourceURL;
result.fromCurrentScript = true;
}
}
return result;
};
var parsedResourceQuery = parseURL(__resourceQuery);
var enabledFeatures = {
"Hot Module Replacement": false,
"Live Reloading": false,
Progress: false,
Overlay: false
};
/** @type {Options} */
var options = {
hot: false,
liveReload: false,
progress: false,
overlay: false
};
if (parsedResourceQuery.hot === "true") {
options.hot = true;
enabledFeatures["Hot Module Replacement"] = true;
}
if (parsedResourceQuery["live-reload"] === "true") {
options.liveReload = true;
enabledFeatures["Live Reloading"] = true;
}
if (parsedResourceQuery.progress === "true") {
options.progress = true;
enabledFeatures.Progress = true;
}
if (parsedResourceQuery.overlay) {
try {
options.overlay = JSON.parse(parsedResourceQuery.overlay);
} catch (e) {
log.error("Error parsing overlay options from resource query:", e);
}
// Fill in default "true" params for partially-specified objects.
if (_typeof(options.overlay) === "object") {
options.overlay = _objectSpread({
errors: true,
warnings: true,
runtimeErrors: true
}, options.overlay);
decodeOverlayOptions(options.overlay);
}
enabledFeatures.Overlay = options.overlay !== false;
}
if (parsedResourceQuery.logging) {
options.logging = parsedResourceQuery.logging;
}
if (typeof parsedResourceQuery.reconnect !== "undefined") {
options.reconnect = Number(parsedResourceQuery.reconnect);
}
/**
* @param {string} level
*/
var setAllLogLevel = function setAllLogLevel(level) {
// This is needed because the HMR logger operate separately from dev server logger
webpackHotLog.setLogLevel(level === "verbose" || level === "log" ? "info" : level);
setLogLevel(level);
};
if (options.logging) {
setAllLogLevel(options.logging);
}
var logEnabledFeatures = function logEnabledFeatures(features) {
var listEnabledFeatures = Object.keys(features);
if (!features || listEnabledFeatures.length === 0) {
return;
}
var logString = "Server started:";
// Server started: Hot Module Replacement enabled, Live Reloading enabled, Overlay disabled.
for (var i = 0; i < listEnabledFeatures.length; i++) {
var key = listEnabledFeatures[i];
logString += " ".concat(key, " ").concat(features[key] ? "enabled" : "disabled", ",");
}
// replace last comma with a period
logString = logString.slice(0, -1).concat(".");
log.info(logString);
};
logEnabledFeatures(enabledFeatures);
self.addEventListener("beforeunload", function () {
status.isUnloading = true;
});
var overlay = typeof window !== "undefined" ? createOverlay(_typeof(options.overlay) === "object" ? {
trustedTypesPolicyName: options.overlay.trustedTypesPolicyName,
catchRuntimeError: options.overlay.runtimeErrors
} : {
trustedTypesPolicyName: false,
catchRuntimeError: options.overlay
}) : {
send: function send() {}
};
/**
* @param {Options} options
* @param {Status} currentStatus
*/
var reloadApp = function reloadApp(_ref, currentStatus) {
var hot = _ref.hot,
liveReload = _ref.liveReload;
if (currentStatus.isUnloading) {
return;
}
var currentHash = currentStatus.currentHash,
previousHash = currentStatus.previousHash;
var isInitial = currentHash.indexOf(/** @type {string} */previousHash) >= 0;
if (isInitial) {
return;
}
/**
* @param {Window} rootWindow
* @param {number} intervalId
*/
function applyReload(rootWindow, intervalId) {
clearInterval(intervalId);
log.info("App updated. Reloading...");
rootWindow.location.reload();
}
var search = self.location.search.toLowerCase();
var allowToHot = search.indexOf("webpack-dev-server-hot=false") === -1;
var allowToLiveReload = search.indexOf("webpack-dev-server-live-reload=false") === -1;
if (hot && allowToHot) {
log.info("App hot update...");
hotEmitter.emit("webpackHotUpdate", currentStatus.currentHash);
if (typeof self !== "undefined" && self.window) {
// broadcast update to window
self.postMessage("webpackHotUpdate".concat(currentStatus.currentHash), "*");
}
}
// allow refreshing the page only if liveReload isn't disabled
else if (liveReload && allowToLiveReload) {
var rootWindow = self;
// use parent window for reload (in case we're in an iframe with no valid src)
var intervalId = self.setInterval(function () {
if (rootWindow.location.protocol !== "about:") {
// reload immediately if protocol is valid
applyReload(rootWindow, intervalId);
} else {
rootWindow = rootWindow.parent;
if (rootWindow.parent === rootWindow) {
// if parent equals current window we've reached the root which would continue forever, so trigger a reload anyways
applyReload(rootWindow, intervalId);
}
}
});
}
};
var ansiRegex = new RegExp(["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|"), "g");
/**
*
* Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string.
* Adapted from code originally released by Sindre Sorhus
* Licensed the MIT License
*
* @param {string} string
* @return {string}
*/
var stripAnsi = function stripAnsi(string) {
if (typeof string !== "string") {
throw new TypeError("Expected a `string`, got `".concat(_typeof(string), "`"));
}
return string.replace(ansiRegex, "");
};
var onSocketMessage = {
hot: function hot() {
if (parsedResourceQuery.hot === "false") {
return;
}
options.hot = true;
},
liveReload: function liveReload() {
if (parsedResourceQuery["live-reload"] === "false") {
return;
}
options.liveReload = true;
},
invalid: function invalid() {
log.info("App updated. Recompiling...");
// Fixes #1042. overlay doesn't clear if errors are fixed but warnings remain.
if (options.overlay) {
overlay.send({
type: "DISMISS"
});
}
sendMessage("Invalid");
},
/**
* @param {string} hash
*/
hash: function hash(_hash) {
status.previousHash = status.currentHash;
status.currentHash = _hash;
},
logging: setAllLogLevel,
/**
* @param {boolean} value
*/
overlay: function overlay(value) {
if (typeof document === "undefined") {
return;
}
options.overlay = value;
decodeOverlayOptions(options.overlay);
},
/**
* @param {number} value
*/
reconnect: function reconnect(value) {
if (parsedResourceQuery.reconnect === "false") {
return;
}
options.reconnect = value;
},
/**
* @param {boolean} value
*/
progress: function progress(value) {
options.progress = value;
},
/**
* @param {{ pluginName?: string, percent: number, msg: string }} data
*/
"progress-update": function progressUpdate(data) {
if (options.progress) {
log.info("".concat(data.pluginName ? "[".concat(data.pluginName, "] ") : "").concat(data.percent, "% - ").concat(data.msg, "."));
}
if (isProgressSupported()) {
if (typeof options.progress === "string") {
var progress = document.querySelector("wds-progress");
if (!progress) {
defineProgressElement();
progress = document.createElement("wds-progress");
document.body.appendChild(progress);
}
progress.setAttribute("progress", data.percent);
progress.setAttribute("type", options.progress);
}
}
sendMessage("Progress", data);
},
"still-ok": function stillOk() {
log.info("Nothing changed.");
if (options.overlay) {
overlay.send({
type: "DISMISS"
});
}
sendMessage("StillOk");
},
ok: function ok() {
sendMessage("Ok");
if (options.overlay) {
overlay.send({
type: "DISMISS"
});
}
reloadApp(options, status);
},
/**
* @param {string} file
*/
"static-changed": function staticChanged(file) {
log.info("".concat(file ? "\"".concat(file, "\"") : "Content", " from static directory was changed. Reloading..."));
self.location.reload();
},
/**
* @param {Error[]} warnings
* @param {any} params
*/
warnings: function warnings(_warnings, params) {
log.warn("Warnings while compiling.");
var printableWarnings = _warnings.map(function (error) {
var _formatProblem = formatProblem("warning", error),
header = _formatProblem.header,
body = _formatProblem.body;
return "".concat(header, "\n").concat(stripAnsi(body));
});
sendMessage("Warnings", printableWarnings);
for (var i = 0; i < printableWarnings.length; i++) {
log.warn(printableWarnings[i]);
}
var overlayWarningsSetting = typeof options.overlay === "boolean" ? options.overlay : options.overlay && options.overlay.warnings;
if (overlayWarningsSetting) {
var warningsToDisplay = typeof overlayWarningsSetting === "function" ? _warnings.filter(overlayWarningsSetting) : _warnings;
if (warningsToDisplay.length) {
overlay.send({
type: "BUILD_ERROR",
level: "warning",
messages: _warnings
});
}
}
if (params && params.preventReloading) {
return;
}
reloadApp(options, status);
},
/**
* @param {Error[]} errors
*/
errors: function errors(_errors) {
log.error("Errors while compiling. Reload prevented.");
var printableErrors = _errors.map(function (error) {
var _formatProblem2 = formatProblem("error", error),
header = _formatProblem2.header,
body = _formatProblem2.body;
return "".concat(header, "\n").concat(stripAnsi(body));
});
sendMessage("Errors", printableErrors);
for (var i = 0; i < printableErrors.length; i++) {
log.error(printableErrors[i]);
}
var overlayErrorsSettings = typeof options.overlay === "boolean" ? options.overlay : options.overlay && options.overlay.errors;
if (overlayErrorsSettings) {
var errorsToDisplay = typeof overlayErrorsSettings === "function" ? _errors.filter(overlayErrorsSettings) : _errors;
if (errorsToDisplay.length) {
overlay.send({
type: "BUILD_ERROR",
level: "error",
messages: _errors
});
}
}
},
/**
* @param {Error} error
*/
error: function error(_error) {
log.error(_error);
},
close: function close() {
log.info("Disconnected!");
if (options.overlay) {
overlay.send({
type: "DISMISS"
});
}
sendMessage("Close");
}
};
/**
* @param {{ protocol?: string, auth?: string, hostname?: string, port?: string, pathname?: string, search?: string, hash?: string, slashes?: boolean }} objURL
* @returns {string}
*/
var formatURL = function formatURL(objURL) {
var protocol = objURL.protocol || "";
if (protocol && protocol.substr(-1) !== ":") {
protocol += ":";
}
var auth = objURL.auth || "";
if (auth) {
auth = encodeURIComponent(auth);
auth = auth.replace(/%3A/i, ":");
auth += "@";
}
var host = "";
if (objURL.hostname) {
host = auth + (objURL.hostname.indexOf(":") === -1 ? objURL.hostname : "[".concat(objURL.hostname, "]"));
if (objURL.port) {
host += ":".concat(objURL.port);
}
}
var pathname = objURL.pathname || "";
if (objURL.slashes) {
host = "//".concat(host || "");
if (pathname && pathname.charAt(0) !== "/") {
pathname = "/".concat(pathname);
}
} else if (!host) {
host = "";
}
var search = objURL.search || "";
if (search && search.charAt(0) !== "?") {
search = "?".concat(search);
}
var hash = objURL.hash || "";
if (hash && hash.charAt(0) !== "#") {
hash = "#".concat(hash);
}
pathname = pathname.replace(/[?#]/g,
/**
* @param {string} match
* @returns {string}
*/
function (match) {
return encodeURIComponent(match);
});
search = search.replace("#", "%23");
return "".concat(protocol).concat(host).concat(pathname).concat(search).concat(hash);
};
/**
* @param {URL & { fromCurrentScript?: boolean }} parsedURL
* @returns {string}
*/
var createSocketURL = function createSocketURL(parsedURL) {
var hostname = parsedURL.hostname;
// Node.js module parses it as `::`
// `new URL(urlString, [baseURLString])` parses it as '[::]'
var isInAddrAny = hostname === "0.0.0.0" || hostname === "::" || hostname === "[::]";
// why do we need this check?
// hostname n/a for file protocol (example, when using electron, ionic)
// see: https://github.com/webpack/webpack-dev-server/pull/384
if (isInAddrAny && self.location.hostname && self.location.protocol.indexOf("http") === 0) {
hostname = self.location.hostname;
}
var socketURLProtocol = parsedURL.protocol || self.location.protocol;
// When https is used in the app, secure web sockets are always necessary because the browser doesn't accept non-secure web sockets.
if (socketURLProtocol === "auto:" || hostname && isInAddrAny && self.location.protocol === "https:") {
socketURLProtocol = self.location.protocol;
}
socketURLProtocol = socketURLProtocol.replace(/^(?:http|.+-extension|file)/i, "ws");
var socketURLAuth = "";
// `new URL(urlString, [baseURLstring])` doesn't have `auth` property
// Parse authentication credentials in case we need them
if (parsedURL.username) {
socketURLAuth = parsedURL.username;
// Since HTTP basic authentication does not allow empty username,
// we only include password if the username is not empty.
if (parsedURL.password) {
// Result: <username>:<password>
socketURLAuth = socketURLAuth.concat(":", parsedURL.password);
}
}
// In case the host is a raw IPv6 address, it can be enclosed in
// the brackets as the brackets are needed in the final URL string.
// Need to remove those as url.format blindly adds its own set of brackets
// if the host string contains colons. That would lead to non-working
// double brackets (e.g. [[::]]) host
//
// All of these web socket url params are optionally passed in through resourceQuery,
// so we need to fall back to the default if they are not provided
var socketURLHostname = (hostname || self.location.hostname || "localhost").replace(/^\[(.*)\]$/, "$1");
var socketURLPort = parsedURL.port;
if (!socketURLPort || socketURLPort === "0") {
socketURLPort = self.location.port;
}
// If path is provided it'll be passed in via the resourceQuery as a
// query param so it has to be parsed out of the querystring in order for the
// client to open the socket to the correct location.
var socketURLPathname = "/ws";
if (parsedURL.pathname && !parsedURL.fromCurrentScript) {
socketURLPathname = parsedURL.pathname;
}
return formatURL({
protocol: socketURLProtocol,
auth: socketURLAuth,
hostname: socketURLHostname,
port: socketURLPort,
pathname: socketURLPathname,
slashes: true
});
};
var socketURL = createSocketURL(parsedResourceQuery);
socket(socketURL, onSocketMessage, options.reconnect);
export { getCurrentScriptSource, parseURL, createSocketURL };

View File

@@ -0,0 +1,824 @@
/******/ (function() { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./client-src/modules/logger/tapable.js":
/*!**********************************************!*\
!*** ./client-src/modules/logger/tapable.js ***!
\**********************************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ SyncBailHook: function() { return /* binding */ SyncBailHook; }
/* harmony export */ });
function SyncBailHook() {
return {
call: function call() {}
};
}
/**
* Client stub for tapable SyncBailHook
*/
// eslint-disable-next-line import/prefer-default-export
/***/ }),
/***/ "./node_modules/webpack/lib/logging/Logger.js":
/*!****************************************************!*\
!*** ./node_modules/webpack/lib/logging/Logger.js ***!
\****************************************************/
/***/ (function(module) {
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && "symbol" == typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && o.constructor === (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && o !== (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function _toConsumableArray(r) {
return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _iterableToArray(r) {
if ("undefined" != typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && null != r[(typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator] || null != r["@@iterator"]) return Array.from(r);
}
function _arrayWithoutHoles(r) {
if (Array.isArray(r)) return _arrayLikeToArray(r);
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _classCallCheck(a, n) {
if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
var o = r[t];
o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
}
}
function _createClass(e, r, t) {
return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
writable: !1
}), e;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t;
var e = t[(typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
var LogType = Object.freeze({
error: (/** @type {"error"} */"error"),
// message, c style arguments
warn: (/** @type {"warn"} */"warn"),
// message, c style arguments
info: (/** @type {"info"} */"info"),
// message, c style arguments
log: (/** @type {"log"} */"log"),
// message, c style arguments
debug: (/** @type {"debug"} */"debug"),
// message, c style arguments
trace: (/** @type {"trace"} */"trace"),
// no arguments
group: (/** @type {"group"} */"group"),
// [label]
groupCollapsed: (/** @type {"groupCollapsed"} */"groupCollapsed"),
// [label]
groupEnd: (/** @type {"groupEnd"} */"groupEnd"),
// [label]
profile: (/** @type {"profile"} */"profile"),
// [profileName]
profileEnd: (/** @type {"profileEnd"} */"profileEnd"),
// [profileName]
time: (/** @type {"time"} */"time"),
// name, time as [seconds, nanoseconds]
clear: (/** @type {"clear"} */"clear"),
// no arguments
status: (/** @type {"status"} */"status") // message, arguments
});
module.exports.LogType = LogType;
/** @typedef {typeof LogType[keyof typeof LogType]} LogTypeEnum */
var LOG_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger raw log method");
var TIMERS_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger times");
var TIMERS_AGGREGATES_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger aggregated times");
var WebpackLogger = /*#__PURE__*/function () {
/**
* @param {(type: LogTypeEnum, args?: EXPECTED_ANY[]) => void} log log function
* @param {(name: string | (() => string)) => WebpackLogger} getChildLogger function to create child logger
*/
function WebpackLogger(log, getChildLogger) {
_classCallCheck(this, WebpackLogger);
this[LOG_SYMBOL] = log;
this.getChildLogger = getChildLogger;
}
/**
* @param {...EXPECTED_ANY} args args
*/
return _createClass(WebpackLogger, [{
key: "error",
value: function error() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
this[LOG_SYMBOL](LogType.error, args);
}
/**
* @param {...EXPECTED_ANY} args args
*/
}, {
key: "warn",
value: function warn() {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
this[LOG_SYMBOL](LogType.warn, args);
}
/**
* @param {...EXPECTED_ANY} args args
*/
}, {
key: "info",
value: function info() {
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
this[LOG_SYMBOL](LogType.info, args);
}
/**
* @param {...EXPECTED_ANY} args args
*/
}, {
key: "log",
value: function log() {
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
args[_key4] = arguments[_key4];
}
this[LOG_SYMBOL](LogType.log, args);
}
/**
* @param {...EXPECTED_ANY} args args
*/
}, {
key: "debug",
value: function debug() {
for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
args[_key5] = arguments[_key5];
}
this[LOG_SYMBOL](LogType.debug, args);
}
/**
* @param {EXPECTED_ANY} assertion assertion
* @param {...EXPECTED_ANY} args args
*/
}, {
key: "assert",
value: function assert(assertion) {
if (!assertion) {
for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
args[_key6 - 1] = arguments[_key6];
}
this[LOG_SYMBOL](LogType.error, args);
}
}
}, {
key: "trace",
value: function trace() {
this[LOG_SYMBOL](LogType.trace, ["Trace"]);
}
}, {
key: "clear",
value: function clear() {
this[LOG_SYMBOL](LogType.clear);
}
/**
* @param {...EXPECTED_ANY} args args
*/
}, {
key: "status",
value: function status() {
for (var _len7 = arguments.length, args = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
args[_key7] = arguments[_key7];
}
this[LOG_SYMBOL](LogType.status, args);
}
/**
* @param {...EXPECTED_ANY} args args
*/
}, {
key: "group",
value: function group() {
for (var _len8 = arguments.length, args = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
args[_key8] = arguments[_key8];
}
this[LOG_SYMBOL](LogType.group, args);
}
/**
* @param {...EXPECTED_ANY} args args
*/
}, {
key: "groupCollapsed",
value: function groupCollapsed() {
for (var _len9 = arguments.length, args = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) {
args[_key9] = arguments[_key9];
}
this[LOG_SYMBOL](LogType.groupCollapsed, args);
}
}, {
key: "groupEnd",
value: function groupEnd() {
this[LOG_SYMBOL](LogType.groupEnd);
}
/**
* @param {string=} label label
*/
}, {
key: "profile",
value: function profile(label) {
this[LOG_SYMBOL](LogType.profile, [label]);
}
/**
* @param {string=} label label
*/
}, {
key: "profileEnd",
value: function profileEnd(label) {
this[LOG_SYMBOL](LogType.profileEnd, [label]);
}
/**
* @param {string} label label
*/
}, {
key: "time",
value: function time(label) {
/** @type {Map<string | undefined, [number, number]>} */
this[TIMERS_SYMBOL] = this[TIMERS_SYMBOL] || new Map();
this[TIMERS_SYMBOL].set(label, process.hrtime());
}
/**
* @param {string=} label label
*/
}, {
key: "timeLog",
value: function timeLog(label) {
var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label);
if (!prev) {
throw new Error("No such label '".concat(label, "' for WebpackLogger.timeLog()"));
}
var time = process.hrtime(prev);
this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time)));
}
/**
* @param {string=} label label
*/
}, {
key: "timeEnd",
value: function timeEnd(label) {
var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label);
if (!prev) {
throw new Error("No such label '".concat(label, "' for WebpackLogger.timeEnd()"));
}
var time = process.hrtime(prev);
/** @type {Map<string | undefined, [number, number]>} */
this[TIMERS_SYMBOL].delete(label);
this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time)));
}
/**
* @param {string=} label label
*/
}, {
key: "timeAggregate",
value: function timeAggregate(label) {
var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label);
if (!prev) {
throw new Error("No such label '".concat(label, "' for WebpackLogger.timeAggregate()"));
}
var time = process.hrtime(prev);
/** @type {Map<string | undefined, [number, number]>} */
this[TIMERS_SYMBOL].delete(label);
/** @type {Map<string | undefined, [number, number]>} */
this[TIMERS_AGGREGATES_SYMBOL] = this[TIMERS_AGGREGATES_SYMBOL] || new Map();
var current = this[TIMERS_AGGREGATES_SYMBOL].get(label);
if (current !== undefined) {
if (time[1] + current[1] > 1e9) {
time[0] += current[0] + 1;
time[1] = time[1] - 1e9 + current[1];
} else {
time[0] += current[0];
time[1] += current[1];
}
}
this[TIMERS_AGGREGATES_SYMBOL].set(label, time);
}
/**
* @param {string=} label label
*/
}, {
key: "timeAggregateEnd",
value: function timeAggregateEnd(label) {
if (this[TIMERS_AGGREGATES_SYMBOL] === undefined) return;
var time = this[TIMERS_AGGREGATES_SYMBOL].get(label);
if (time === undefined) return;
this[TIMERS_AGGREGATES_SYMBOL].delete(label);
this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time)));
}
}]);
}();
module.exports.Logger = WebpackLogger;
/***/ }),
/***/ "./node_modules/webpack/lib/logging/createConsoleLogger.js":
/*!*****************************************************************!*\
!*** ./node_modules/webpack/lib/logging/createConsoleLogger.js ***!
\*****************************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && r[(typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (i = (t = t.call(r)).next, 0 === l) {
if (Object(t) !== t) return;
f = !1;
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = !0, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) return r;
}
function _toConsumableArray(r) {
return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _iterableToArray(r) {
if ("undefined" != typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && null != r[(typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator] || null != r["@@iterator"]) return Array.from(r);
}
function _arrayWithoutHoles(r) {
if (Array.isArray(r)) return _arrayLikeToArray(r);
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && "symbol" == typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && o.constructor === (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) && o !== (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var _require = __webpack_require__(/*! ./Logger */ "./node_modules/webpack/lib/logging/Logger.js"),
LogType = _require.LogType;
/** @typedef {import("../../declarations/WebpackOptions").FilterItemTypes} FilterItemTypes */
/** @typedef {import("../../declarations/WebpackOptions").FilterTypes} FilterTypes */
/** @typedef {import("./Logger").LogTypeEnum} LogTypeEnum */
/** @typedef {(item: string) => boolean} FilterFunction */
/** @typedef {(value: string, type: LogTypeEnum, args?: EXPECTED_ANY[]) => void} LoggingFunction */
/**
* @typedef {object} LoggerConsole
* @property {() => void} clear
* @property {() => void} trace
* @property {(...args: EXPECTED_ANY[]) => void} info
* @property {(...args: EXPECTED_ANY[]) => void} log
* @property {(...args: EXPECTED_ANY[]) => void} warn
* @property {(...args: EXPECTED_ANY[]) => void} error
* @property {(...args: EXPECTED_ANY[]) => void=} debug
* @property {(...args: EXPECTED_ANY[]) => void=} group
* @property {(...args: EXPECTED_ANY[]) => void=} groupCollapsed
* @property {(...args: EXPECTED_ANY[]) => void=} groupEnd
* @property {(...args: EXPECTED_ANY[]) => void=} status
* @property {(...args: EXPECTED_ANY[]) => void=} profile
* @property {(...args: EXPECTED_ANY[]) => void=} profileEnd
* @property {(...args: EXPECTED_ANY[]) => void=} logTime
*/
/**
* @typedef {object} LoggerOptions
* @property {false|true|"none"|"error"|"warn"|"info"|"log"|"verbose"} level loglevel
* @property {FilterTypes|boolean} debug filter for debug logging
* @property {LoggerConsole} console the console to log to
*/
/**
* @param {FilterItemTypes} item an input item
* @returns {FilterFunction | undefined} filter function
*/
var filterToFunction = function filterToFunction(item) {
if (typeof item === "string") {
var regExp = new RegExp("[\\\\/]".concat(item.replace(/[-[\]{}()*+?.\\^$|]/g, "\\$&"), "([\\\\/]|$|!|\\?)"));
return function (ident) {
return regExp.test(ident);
};
}
if (item && _typeof(item) === "object" && typeof item.test === "function") {
return function (ident) {
return item.test(ident);
};
}
if (typeof item === "function") {
return item;
}
if (typeof item === "boolean") {
return function () {
return item;
};
}
};
/**
* @enum {number}
*/
var LogLevel = {
none: 6,
false: 6,
error: 5,
warn: 4,
info: 3,
log: 2,
true: 2,
verbose: 1
};
/**
* @param {LoggerOptions} options options object
* @returns {LoggingFunction} logging function
*/
module.exports = function (_ref) {
var _ref$level = _ref.level,
level = _ref$level === void 0 ? "info" : _ref$level,
_ref$debug = _ref.debug,
debug = _ref$debug === void 0 ? false : _ref$debug,
console = _ref.console;
var debugFilters = /** @type {FilterFunction[]} */
typeof debug === "boolean" ? [function () {
return debug;
}] : /** @type {FilterItemTypes[]} */[].concat(debug).map(filterToFunction);
var loglevel = LogLevel["".concat(level)] || 0;
/**
* @param {string} name name of the logger
* @param {LogTypeEnum} type type of the log entry
* @param {EXPECTED_ANY[]=} args arguments of the log entry
* @returns {void}
*/
var logger = function logger(name, type, args) {
var labeledArgs = function labeledArgs() {
if (Array.isArray(args)) {
if (args.length > 0 && typeof args[0] === "string") {
return ["[".concat(name, "] ").concat(args[0])].concat(_toConsumableArray(args.slice(1)));
}
return ["[".concat(name, "]")].concat(_toConsumableArray(args));
}
return [];
};
var debug = debugFilters.some(function (f) {
return f(name);
});
switch (type) {
case LogType.debug:
if (!debug) return;
if (typeof console.debug === "function") {
console.debug.apply(console, _toConsumableArray(labeledArgs()));
} else {
console.log.apply(console, _toConsumableArray(labeledArgs()));
}
break;
case LogType.log:
if (!debug && loglevel > LogLevel.log) return;
console.log.apply(console, _toConsumableArray(labeledArgs()));
break;
case LogType.info:
if (!debug && loglevel > LogLevel.info) return;
console.info.apply(console, _toConsumableArray(labeledArgs()));
break;
case LogType.warn:
if (!debug && loglevel > LogLevel.warn) return;
console.warn.apply(console, _toConsumableArray(labeledArgs()));
break;
case LogType.error:
if (!debug && loglevel > LogLevel.error) return;
console.error.apply(console, _toConsumableArray(labeledArgs()));
break;
case LogType.trace:
if (!debug) return;
console.trace();
break;
case LogType.groupCollapsed:
if (!debug && loglevel > LogLevel.log) return;
if (!debug && loglevel > LogLevel.verbose) {
if (typeof console.groupCollapsed === "function") {
console.groupCollapsed.apply(console, _toConsumableArray(labeledArgs()));
} else {
console.log.apply(console, _toConsumableArray(labeledArgs()));
}
break;
}
// falls through
case LogType.group:
if (!debug && loglevel > LogLevel.log) return;
if (typeof console.group === "function") {
console.group.apply(console, _toConsumableArray(labeledArgs()));
} else {
console.log.apply(console, _toConsumableArray(labeledArgs()));
}
break;
case LogType.groupEnd:
if (!debug && loglevel > LogLevel.log) return;
if (typeof console.groupEnd === "function") {
console.groupEnd();
}
break;
case LogType.time:
{
if (!debug && loglevel > LogLevel.log) return;
var _args = _slicedToArray(/** @type {[string, number, number]} */
args, 3),
label = _args[0],
start = _args[1],
end = _args[2];
var ms = start * 1000 + end / 1000000;
var msg = "[".concat(name, "] ").concat(label, ": ").concat(ms, " ms");
if (typeof console.logTime === "function") {
console.logTime(msg);
} else {
console.log(msg);
}
break;
}
case LogType.profile:
if (typeof console.profile === "function") {
console.profile.apply(console, _toConsumableArray(labeledArgs()));
}
break;
case LogType.profileEnd:
if (typeof console.profileEnd === "function") {
console.profileEnd.apply(console, _toConsumableArray(labeledArgs()));
}
break;
case LogType.clear:
if (!debug && loglevel > LogLevel.log) return;
if (typeof console.clear === "function") {
console.clear();
}
break;
case LogType.status:
if (!debug && loglevel > LogLevel.info) return;
if (typeof console.status === "function") {
if (!args || args.length === 0) {
console.status();
} else {
console.status.apply(console, _toConsumableArray(labeledArgs()));
}
} else if (args && args.length !== 0) {
console.info.apply(console, _toConsumableArray(labeledArgs()));
}
break;
default:
throw new Error("Unexpected LogType ".concat(type));
}
};
return logger;
};
/***/ }),
/***/ "./node_modules/webpack/lib/logging/runtime.js":
/*!*****************************************************!*\
!*** ./node_modules/webpack/lib/logging/runtime.js ***!
\*****************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function (n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
}
return n;
}, _extends.apply(null, arguments);
}
var _require = __webpack_require__(/*! tapable */ "./client-src/modules/logger/tapable.js"),
SyncBailHook = _require.SyncBailHook;
var _require2 = __webpack_require__(/*! ./Logger */ "./node_modules/webpack/lib/logging/Logger.js"),
Logger = _require2.Logger;
var createConsoleLogger = __webpack_require__(/*! ./createConsoleLogger */ "./node_modules/webpack/lib/logging/createConsoleLogger.js");
/** @type {createConsoleLogger.LoggerOptions} */
var currentDefaultLoggerOptions = {
level: "info",
debug: false,
console: console
};
var currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions);
/**
* @param {string} name name of the logger
* @returns {Logger} a logger
*/
module.exports.getLogger = function (name) {
return new Logger(function (type, args) {
if (module.exports.hooks.log.call(name, type, args) === undefined) {
currentDefaultLogger(name, type, args);
}
}, function (childName) {
return module.exports.getLogger("".concat(name, "/").concat(childName));
});
};
/**
* @param {createConsoleLogger.LoggerOptions} options new options, merge with old options
* @returns {void}
*/
module.exports.configureDefaultLogger = function (options) {
_extends(currentDefaultLoggerOptions, options);
currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions);
};
module.exports.hooks = {
log: new SyncBailHook(["origin", "type", "args"])
};
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ !function() {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
!function() {
/*!********************************************!*\
!*** ./client-src/modules/logger/index.js ***!
\********************************************/
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* reexport default export from named module */ webpack_lib_logging_runtime_js__WEBPACK_IMPORTED_MODULE_0__; }
/* harmony export */ });
/* harmony import */ var webpack_lib_logging_runtime_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webpack/lib/logging/runtime.js */ "./node_modules/webpack/lib/logging/runtime.js");
}();
var __webpack_export_target__ = exports;
for(var __webpack_i__ in __webpack_exports__) __webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__];
if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });
/******/ })()
;

File diff suppressed because it is too large Load Diff

615
node_modules/webpack-dev-server/client/overlay.js generated vendored Normal file
View File

@@ -0,0 +1,615 @@
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
// The error overlay is inspired (and mostly copied) from Create React App (https://github.com/facebookincubator/create-react-app)
// They, in turn, got inspired by webpack-hot-middleware (https://github.com/glenjamin/webpack-hot-middleware).
import ansiHTML from "ansi-html-community";
/**
* @type {(input: string, position: number) => string}
*/
var getCodePoint = String.prototype.codePointAt ? function (input, position) {
return input.codePointAt(position);
} : function (input, position) {
return (input.charCodeAt(position) - 0xd800) * 0x400 + input.charCodeAt(position + 1) - 0xdc00 + 0x10000;
};
/**
* @param {string} macroText
* @param {RegExp} macroRegExp
* @param {(input: string) => string} macroReplacer
* @returns {string}
*/
var replaceUsingRegExp = function replaceUsingRegExp(macroText, macroRegExp, macroReplacer) {
macroRegExp.lastIndex = 0;
var replaceMatch = macroRegExp.exec(macroText);
var replaceResult;
if (replaceMatch) {
replaceResult = "";
var replaceLastIndex = 0;
do {
if (replaceLastIndex !== replaceMatch.index) {
replaceResult += macroText.substring(replaceLastIndex, replaceMatch.index);
}
var replaceInput = replaceMatch[0];
replaceResult += macroReplacer(replaceInput);
replaceLastIndex = replaceMatch.index + replaceInput.length;
// eslint-disable-next-line no-cond-assign
} while (replaceMatch = macroRegExp.exec(macroText));
if (replaceLastIndex !== macroText.length) {
replaceResult += macroText.substring(replaceLastIndex);
}
} else {
replaceResult = macroText;
}
return replaceResult;
};
var references = {
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&apos;",
"&": "&amp;"
};
/**
* @param {string} text text
* @returns {string}
*/
function encode(text) {
if (!text) {
return "";
}
return replaceUsingRegExp(text, /[<>'"&]/g, function (input) {
var result = references[input];
if (!result) {
var code = input.length > 1 ? getCodePoint(input, 0) : input.charCodeAt(0);
result = "&#".concat(code, ";");
}
return result;
});
}
/**
* @typedef {Object} StateDefinitions
* @property {{[event: string]: { target: string; actions?: Array<string> }}} [on]
*/
/**
* @typedef {Object} Options
* @property {{[state: string]: StateDefinitions}} states
* @property {object} context;
* @property {string} initial
*/
/**
* @typedef {Object} Implementation
* @property {{[actionName: string]: (ctx: object, event: any) => object}} actions
*/
/**
* A simplified `createMachine` from `@xstate/fsm` with the following differences:
*
* - the returned machine is technically a "service". No `interpret(machine).start()` is needed.
* - the state definition only support `on` and target must be declared with { target: 'nextState', actions: [] } explicitly.
* - event passed to `send` must be an object with `type` property.
* - actions implementation will be [assign action](https://xstate.js.org/docs/guides/context.html#assign-action) if you return any value.
* Do not return anything if you just want to invoke side effect.
*
* The goal of this custom function is to avoid installing the entire `'xstate/fsm'` package, while enabling modeling using
* state machine. You can copy the first parameter into the editor at https://stately.ai/viz to visualize the state machine.
*
* @param {Options} options
* @param {Implementation} implementation
*/
function createMachine(_ref, _ref2) {
var states = _ref.states,
context = _ref.context,
initial = _ref.initial;
var actions = _ref2.actions;
var currentState = initial;
var currentContext = context;
return {
send: function send(event) {
var currentStateOn = states[currentState].on;
var transitionConfig = currentStateOn && currentStateOn[event.type];
if (transitionConfig) {
currentState = transitionConfig.target;
if (transitionConfig.actions) {
transitionConfig.actions.forEach(function (actName) {
var actionImpl = actions[actName];
var nextContextValue = actionImpl && actionImpl(currentContext, event);
if (nextContextValue) {
currentContext = _objectSpread(_objectSpread({}, currentContext), nextContextValue);
}
});
}
}
}
};
}
/**
* @typedef {Object} ShowOverlayData
* @property {'warning' | 'error'} level
* @property {Array<string | { moduleIdentifier?: string, moduleName?: string, loc?: string, message?: string }>} messages
* @property {'build' | 'runtime'} messageSource
*/
/**
* @typedef {Object} CreateOverlayMachineOptions
* @property {(data: ShowOverlayData) => void} showOverlay
* @property {() => void} hideOverlay
*/
/**
* @param {CreateOverlayMachineOptions} options
*/
var createOverlayMachine = function createOverlayMachine(options) {
var hideOverlay = options.hideOverlay,
showOverlay = options.showOverlay;
return createMachine({
initial: "hidden",
context: {
level: "error",
messages: [],
messageSource: "build"
},
states: {
hidden: {
on: {
BUILD_ERROR: {
target: "displayBuildError",
actions: ["setMessages", "showOverlay"]
},
RUNTIME_ERROR: {
target: "displayRuntimeError",
actions: ["setMessages", "showOverlay"]
}
}
},
displayBuildError: {
on: {
DISMISS: {
target: "hidden",
actions: ["dismissMessages", "hideOverlay"]
},
BUILD_ERROR: {
target: "displayBuildError",
actions: ["appendMessages", "showOverlay"]
}
}
},
displayRuntimeError: {
on: {
DISMISS: {
target: "hidden",
actions: ["dismissMessages", "hideOverlay"]
},
RUNTIME_ERROR: {
target: "displayRuntimeError",
actions: ["appendMessages", "showOverlay"]
},
BUILD_ERROR: {
target: "displayBuildError",
actions: ["setMessages", "showOverlay"]
}
}
}
}
}, {
actions: {
dismissMessages: function dismissMessages() {
return {
messages: [],
level: "error",
messageSource: "build"
};
},
appendMessages: function appendMessages(context, event) {
return {
messages: context.messages.concat(event.messages),
level: event.level || context.level,
messageSource: event.type === "RUNTIME_ERROR" ? "runtime" : "build"
};
},
setMessages: function setMessages(context, event) {
return {
messages: event.messages,
level: event.level || context.level,
messageSource: event.type === "RUNTIME_ERROR" ? "runtime" : "build"
};
},
hideOverlay: hideOverlay,
showOverlay: showOverlay
}
});
};
/**
*
* @param {Error} error
*/
var parseErrorToStacks = function parseErrorToStacks(error) {
if (!error || !(error instanceof Error)) {
throw new Error("parseErrorToStacks expects Error object");
}
if (typeof error.stack === "string") {
return error.stack.split("\n").filter(function (stack) {
return stack !== "Error: ".concat(error.message);
});
}
};
/**
* @callback ErrorCallback
* @param {ErrorEvent} error
* @returns {void}
*/
/**
* @param {ErrorCallback} callback
*/
var listenToRuntimeError = function listenToRuntimeError(callback) {
window.addEventListener("error", callback);
return function cleanup() {
window.removeEventListener("error", callback);
};
};
/**
* @callback UnhandledRejectionCallback
* @param {PromiseRejectionEvent} rejectionEvent
* @returns {void}
*/
/**
* @param {UnhandledRejectionCallback} callback
*/
var listenToUnhandledRejection = function listenToUnhandledRejection(callback) {
window.addEventListener("unhandledrejection", callback);
return function cleanup() {
window.removeEventListener("unhandledrejection", callback);
};
};
// Styles are inspired by `react-error-overlay`
var msgStyles = {
error: {
backgroundColor: "rgba(206, 17, 38, 0.1)",
color: "#fccfcf"
},
warning: {
backgroundColor: "rgba(251, 245, 180, 0.1)",
color: "#fbf5b4"
}
};
var iframeStyle = {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
width: "100vw",
height: "100vh",
border: "none",
"z-index": 9999999999
};
var containerStyle = {
position: "fixed",
boxSizing: "border-box",
left: 0,
top: 0,
right: 0,
bottom: 0,
width: "100vw",
height: "100vh",
fontSize: "large",
padding: "2rem 2rem 4rem 2rem",
lineHeight: "1.2",
whiteSpace: "pre-wrap",
overflow: "auto",
backgroundColor: "rgba(0, 0, 0, 0.9)",
color: "white"
};
var headerStyle = {
color: "#e83b46",
fontSize: "2em",
whiteSpace: "pre-wrap",
fontFamily: "sans-serif",
margin: "0 2rem 2rem 0",
flex: "0 0 auto",
maxHeight: "50%",
overflow: "auto"
};
var dismissButtonStyle = {
color: "#ffffff",
lineHeight: "1rem",
fontSize: "1.5rem",
padding: "1rem",
cursor: "pointer",
position: "absolute",
right: 0,
top: 0,
backgroundColor: "transparent",
border: "none"
};
var msgTypeStyle = {
color: "#e83b46",
fontSize: "1.2em",
marginBottom: "1rem",
fontFamily: "sans-serif"
};
var msgTextStyle = {
lineHeight: "1.5",
fontSize: "1rem",
fontFamily: "Menlo, Consolas, monospace"
};
// ANSI HTML
var colors = {
reset: ["transparent", "transparent"],
black: "181818",
red: "E36049",
green: "B3CB74",
yellow: "FFD080",
blue: "7CAFC2",
magenta: "7FACCA",
cyan: "C3C2EF",
lightgrey: "EBE7E3",
darkgrey: "6D7891"
};
ansiHTML.setColors(colors);
/**
* @param {string} type
* @param {string | { file?: string, moduleName?: string, loc?: string, message?: string; stack?: string[] }} item
* @returns {{ header: string, body: string }}
*/
var formatProblem = function formatProblem(type, item) {
var header = type === "warning" ? "WARNING" : "ERROR";
var body = "";
if (typeof item === "string") {
body += item;
} else {
var file = item.file || "";
// eslint-disable-next-line no-nested-ternary
var moduleName = item.moduleName ? item.moduleName.indexOf("!") !== -1 ? "".concat(item.moduleName.replace(/^(\s|\S)*!/, ""), " (").concat(item.moduleName, ")") : "".concat(item.moduleName) : "";
var loc = item.loc;
header += "".concat(moduleName || file ? " in ".concat(moduleName ? "".concat(moduleName).concat(file ? " (".concat(file, ")") : "") : file).concat(loc ? " ".concat(loc) : "") : "");
body += item.message || "";
}
if (Array.isArray(item.stack)) {
item.stack.forEach(function (stack) {
if (typeof stack === "string") {
body += "\r\n".concat(stack);
}
});
}
return {
header: header,
body: body
};
};
/**
* @typedef {Object} CreateOverlayOptions
* @property {string | null} trustedTypesPolicyName
* @property {boolean | (error: Error) => void} [catchRuntimeError]
*/
/**
*
* @param {CreateOverlayOptions} options
*/
var createOverlay = function createOverlay(options) {
/** @type {HTMLIFrameElement | null | undefined} */
var iframeContainerElement;
/** @type {HTMLDivElement | null | undefined} */
var containerElement;
/** @type {HTMLDivElement | null | undefined} */
var headerElement;
/** @type {Array<(element: HTMLDivElement) => void>} */
var onLoadQueue = [];
/** @type {TrustedTypePolicy | undefined} */
var overlayTrustedTypesPolicy;
/**
*
* @param {HTMLElement} element
* @param {CSSStyleDeclaration} style
*/
function applyStyle(element, style) {
Object.keys(style).forEach(function (prop) {
element.style[prop] = style[prop];
});
}
/**
* @param {string | null} trustedTypesPolicyName
*/
function createContainer(trustedTypesPolicyName) {
// Enable Trusted Types if they are available in the current browser.
if (window.trustedTypes) {
overlayTrustedTypesPolicy = window.trustedTypes.createPolicy(trustedTypesPolicyName || "webpack-dev-server#overlay", {
createHTML: function createHTML(value) {
return value;
}
});
}
iframeContainerElement = document.createElement("iframe");
iframeContainerElement.id = "webpack-dev-server-client-overlay";
iframeContainerElement.src = "about:blank";
applyStyle(iframeContainerElement, iframeStyle);
iframeContainerElement.onload = function () {
var contentElement = /** @type {Document} */
(/** @type {HTMLIFrameElement} */
iframeContainerElement.contentDocument).createElement("div");
containerElement = /** @type {Document} */
(/** @type {HTMLIFrameElement} */
iframeContainerElement.contentDocument).createElement("div");
contentElement.id = "webpack-dev-server-client-overlay-div";
applyStyle(contentElement, containerStyle);
headerElement = document.createElement("div");
headerElement.innerText = "Compiled with problems:";
applyStyle(headerElement, headerStyle);
var closeButtonElement = document.createElement("button");
applyStyle(closeButtonElement, dismissButtonStyle);
closeButtonElement.innerText = "×";
closeButtonElement.ariaLabel = "Dismiss";
closeButtonElement.addEventListener("click", function () {
// eslint-disable-next-line no-use-before-define
overlayService.send({
type: "DISMISS"
});
});
contentElement.appendChild(headerElement);
contentElement.appendChild(closeButtonElement);
contentElement.appendChild(containerElement);
/** @type {Document} */
(/** @type {HTMLIFrameElement} */
iframeContainerElement.contentDocument).body.appendChild(contentElement);
onLoadQueue.forEach(function (onLoad) {
onLoad(/** @type {HTMLDivElement} */contentElement);
});
onLoadQueue = [];
/** @type {HTMLIFrameElement} */
iframeContainerElement.onload = null;
};
document.body.appendChild(iframeContainerElement);
}
/**
* @param {(element: HTMLDivElement) => void} callback
* @param {string | null} trustedTypesPolicyName
*/
function ensureOverlayExists(callback, trustedTypesPolicyName) {
if (containerElement) {
containerElement.innerHTML = overlayTrustedTypesPolicy ? overlayTrustedTypesPolicy.createHTML("") : "";
// Everything is ready, call the callback right away.
callback(containerElement);
return;
}
onLoadQueue.push(callback);
if (iframeContainerElement) {
return;
}
createContainer(trustedTypesPolicyName);
}
// Successful compilation.
function hide() {
if (!iframeContainerElement) {
return;
}
// Clean up and reset internal state.
document.body.removeChild(iframeContainerElement);
iframeContainerElement = null;
containerElement = null;
}
// Compilation with errors (e.g. syntax error or missing modules).
/**
* @param {string} type
* @param {Array<string | { moduleIdentifier?: string, moduleName?: string, loc?: string, message?: string }>} messages
* @param {string | null} trustedTypesPolicyName
* @param {'build' | 'runtime'} messageSource
*/
function show(type, messages, trustedTypesPolicyName, messageSource) {
ensureOverlayExists(function () {
headerElement.innerText = messageSource === "runtime" ? "Uncaught runtime errors:" : "Compiled with problems:";
messages.forEach(function (message) {
var entryElement = document.createElement("div");
var msgStyle = type === "warning" ? msgStyles.warning : msgStyles.error;
applyStyle(entryElement, _objectSpread(_objectSpread({}, msgStyle), {}, {
padding: "1rem 1rem 1.5rem 1rem"
}));
var typeElement = document.createElement("div");
var _formatProblem = formatProblem(type, message),
header = _formatProblem.header,
body = _formatProblem.body;
typeElement.innerText = header;
applyStyle(typeElement, msgTypeStyle);
if (message.moduleIdentifier) {
applyStyle(typeElement, {
cursor: "pointer"
});
// element.dataset not supported in IE
typeElement.setAttribute("data-can-open", true);
typeElement.addEventListener("click", function () {
fetch("/webpack-dev-server/open-editor?fileName=".concat(message.moduleIdentifier));
});
}
// Make it look similar to our terminal.
var text = ansiHTML(encode(body));
var messageTextNode = document.createElement("div");
applyStyle(messageTextNode, msgTextStyle);
messageTextNode.innerHTML = overlayTrustedTypesPolicy ? overlayTrustedTypesPolicy.createHTML(text) : text;
entryElement.appendChild(typeElement);
entryElement.appendChild(messageTextNode);
/** @type {HTMLDivElement} */
containerElement.appendChild(entryElement);
});
}, trustedTypesPolicyName);
}
var overlayService = createOverlayMachine({
showOverlay: function showOverlay(_ref3) {
var _ref3$level = _ref3.level,
level = _ref3$level === void 0 ? "error" : _ref3$level,
messages = _ref3.messages,
messageSource = _ref3.messageSource;
return show(level, messages, options.trustedTypesPolicyName, messageSource);
},
hideOverlay: hide
});
if (options.catchRuntimeError) {
/**
* @param {Error | undefined} error
* @param {string} fallbackMessage
*/
var handleError = function handleError(error, fallbackMessage) {
var errorObject = error instanceof Error ? error : new Error(error || fallbackMessage);
var shouldDisplay = typeof options.catchRuntimeError === "function" ? options.catchRuntimeError(errorObject) : true;
if (shouldDisplay) {
overlayService.send({
type: "RUNTIME_ERROR",
messages: [{
message: errorObject.message,
stack: parseErrorToStacks(errorObject)
}]
});
}
};
listenToRuntimeError(function (errorEvent) {
// error property may be empty in older browser like IE
var error = errorEvent.error,
message = errorEvent.message;
if (!error && !message) {
return;
}
// if error stack indicates a React error boundary caught the error, do not show overlay.
if (error && error.stack && error.stack.includes("invokeGuardedCallbackDev")) {
return;
}
handleError(error, message);
});
listenToUnhandledRejection(function (promiseRejectionEvent) {
var reason = promiseRejectionEvent.reason;
handleError(reason, "Unknown promise rejection reason");
});
}
return overlayService;
};
export { formatProblem, createOverlay };

125
node_modules/webpack-dev-server/client/progress.js generated vendored Normal file
View File

@@ -0,0 +1,125 @@
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
function _wrapNativeSuper(t) { var r = "function" == typeof Map ? new Map() : void 0; return _wrapNativeSuper = function _wrapNativeSuper(t) { if (null === t || !_isNativeFunction(t)) return t; if ("function" != typeof t) throw new TypeError("Super expression must either be null or a function"); if (void 0 !== r) { if (r.has(t)) return r.get(t); r.set(t, Wrapper); } function Wrapper() { return _construct(t, arguments, _getPrototypeOf(this).constructor); } return Wrapper.prototype = Object.create(t.prototype, { constructor: { value: Wrapper, enumerable: !1, writable: !0, configurable: !0 } }), _setPrototypeOf(Wrapper, t); }, _wrapNativeSuper(t); }
function _construct(t, e, r) { if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments); var o = [null]; o.push.apply(o, e); var p = new (t.bind.apply(t, o))(); return r && _setPrototypeOf(p, r.prototype), p; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _isNativeFunction(t) { try { return -1 !== Function.toString.call(t).indexOf("[native code]"); } catch (n) { return "function" == typeof t; } }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
export function isProgressSupported() {
return "customElements" in self && !!HTMLElement.prototype.attachShadow;
}
export function defineProgressElement() {
var _WebpackDevServerProgress;
if (customElements.get("wds-progress")) {
return;
}
var _WebpackDevServerProgress_brand = /*#__PURE__*/new WeakSet();
var WebpackDevServerProgress = /*#__PURE__*/function (_HTMLElement) {
function WebpackDevServerProgress() {
var _this;
_classCallCheck(this, WebpackDevServerProgress);
_this = _callSuper(this, WebpackDevServerProgress);
_classPrivateMethodInitSpec(_this, _WebpackDevServerProgress_brand);
_this.attachShadow({
mode: "open"
});
_this.maxDashOffset = -219.99078369140625;
_this.animationTimer = null;
return _this;
}
_inherits(WebpackDevServerProgress, _HTMLElement);
return _createClass(WebpackDevServerProgress, [{
key: "connectedCallback",
value: function connectedCallback() {
_assertClassBrand(_WebpackDevServerProgress_brand, this, _reset).call(this);
}
}, {
key: "attributeChangedCallback",
value: function attributeChangedCallback(name, oldValue, newValue) {
if (name === "progress") {
_assertClassBrand(_WebpackDevServerProgress_brand, this, _update).call(this, Number(newValue));
} else if (name === "type") {
_assertClassBrand(_WebpackDevServerProgress_brand, this, _reset).call(this);
}
}
}], [{
key: "observedAttributes",
get: function get() {
return ["progress", "type"];
}
}]);
}(/*#__PURE__*/_wrapNativeSuper(HTMLElement));
_WebpackDevServerProgress = WebpackDevServerProgress;
function _reset() {
var _this$getAttribute, _Number;
clearTimeout(this.animationTimer);
this.animationTimer = null;
var typeAttr = (_this$getAttribute = this.getAttribute("type")) === null || _this$getAttribute === void 0 ? void 0 : _this$getAttribute.toLowerCase();
this.type = typeAttr === "circular" ? "circular" : "linear";
var innerHTML = this.type === "circular" ? _circularTemplate.call(_WebpackDevServerProgress) : _linearTemplate.call(_WebpackDevServerProgress);
this.shadowRoot.innerHTML = innerHTML;
this.initialProgress = (_Number = Number(this.getAttribute("progress"))) !== null && _Number !== void 0 ? _Number : 0;
_assertClassBrand(_WebpackDevServerProgress_brand, this, _update).call(this, this.initialProgress);
}
function _circularTemplate() {
return "\n <style>\n :host {\n width: 200px;\n height: 200px;\n position: fixed;\n right: 5%;\n top: 5%;\n transition: opacity .25s ease-in-out;\n z-index: 2147483645;\n }\n\n circle {\n fill: #282d35;\n }\n\n path {\n fill: rgba(0, 0, 0, 0);\n stroke: rgb(186, 223, 172);\n stroke-dasharray: 219.99078369140625;\n stroke-dashoffset: -219.99078369140625;\n stroke-width: 10;\n transform: rotate(90deg) translate(0px, -80px);\n }\n\n text {\n font-family: 'Open Sans', sans-serif;\n font-size: 18px;\n fill: #ffffff;\n dominant-baseline: middle;\n text-anchor: middle;\n }\n\n tspan#percent-super {\n fill: #bdc3c7;\n font-size: 0.45em;\n baseline-shift: 10%;\n }\n\n @keyframes fade {\n 0% { opacity: 1; transform: scale(1); }\n 100% { opacity: 0; transform: scale(0); }\n }\n\n .disappear {\n animation: fade 0.3s;\n animation-fill-mode: forwards;\n animation-delay: 0.5s;\n }\n\n .hidden {\n display: none;\n }\n </style>\n <svg id=\"progress\" class=\"hidden noselect\" viewBox=\"0 0 80 80\">\n <circle cx=\"50%\" cy=\"50%\" r=\"35\"></circle>\n <path d=\"M5,40a35,35 0 1,0 70,0a35,35 0 1,0 -70,0\"></path>\n <text x=\"50%\" y=\"51%\">\n <tspan id=\"percent-value\">0</tspan>\n <tspan id=\"percent-super\">%</tspan>\n </text>\n </svg>\n ";
}
function _linearTemplate() {
return "\n <style>\n :host {\n position: fixed;\n top: 0;\n left: 0;\n height: 4px;\n width: 100vw;\n z-index: 2147483645;\n }\n\n #bar {\n width: 0%;\n height: 4px;\n background-color: rgb(186, 223, 172);\n }\n\n @keyframes fade {\n 0% { opacity: 1; }\n 100% { opacity: 0; }\n }\n\n .disappear {\n animation: fade 0.3s;\n animation-fill-mode: forwards;\n animation-delay: 0.5s;\n }\n\n .hidden {\n display: none;\n }\n </style>\n <div id=\"progress\"></div>\n ";
}
function _update(percent) {
var element = this.shadowRoot.querySelector("#progress");
if (this.type === "circular") {
var path = this.shadowRoot.querySelector("path");
var value = this.shadowRoot.querySelector("#percent-value");
var offset = (100 - percent) / 100 * this.maxDashOffset;
path.style.strokeDashoffset = offset;
value.textContent = percent;
} else {
element.style.width = "".concat(percent, "%");
}
if (percent >= 100) {
_assertClassBrand(_WebpackDevServerProgress_brand, this, _hide).call(this);
} else if (percent > 0) {
_assertClassBrand(_WebpackDevServerProgress_brand, this, _show).call(this);
}
}
function _show() {
var element = this.shadowRoot.querySelector("#progress");
element.classList.remove("hidden");
}
function _hide() {
var _this2 = this;
var element = this.shadowRoot.querySelector("#progress");
if (this.type === "circular") {
element.classList.add("disappear");
element.addEventListener("animationend", function () {
element.classList.add("hidden");
_assertClassBrand(_WebpackDevServerProgress_brand, _this2, _update).call(_this2, 0);
}, {
once: true
});
} else if (this.type === "linear") {
element.classList.add("disappear");
this.animationTimer = setTimeout(function () {
element.classList.remove("disappear");
element.classList.add("hidden");
element.style.width = "0%";
_this2.animationTimer = null;
}, 800);
}
}
customElements.define("wds-progress", WebpackDevServerProgress);
}

70
node_modules/webpack-dev-server/client/socket.js generated vendored Normal file
View File

@@ -0,0 +1,70 @@
/* global __webpack_dev_server_client__ */
import WebSocketClient from "./clients/WebSocketClient.js";
import { log } from "./utils/log.js";
// this WebsocketClient is here as a default fallback, in case the client is not injected
/* eslint-disable camelcase */
var Client =
// eslint-disable-next-line no-nested-ternary
typeof __webpack_dev_server_client__ !== "undefined" ? typeof __webpack_dev_server_client__.default !== "undefined" ? __webpack_dev_server_client__.default : __webpack_dev_server_client__ : WebSocketClient;
/* eslint-enable camelcase */
var retries = 0;
var maxRetries = 10;
// Initialized client is exported so external consumers can utilize the same instance
// It is mutable to enforce singleton
// eslint-disable-next-line import/no-mutable-exports
export var client = null;
var timeout;
/**
* @param {string} url
* @param {{ [handler: string]: (data?: any, params?: any) => any }} handlers
* @param {number} [reconnect]
*/
var socket = function initSocket(url, handlers, reconnect) {
client = new Client(url);
client.onOpen(function () {
retries = 0;
if (timeout) {
clearTimeout(timeout);
}
if (typeof reconnect !== "undefined") {
maxRetries = reconnect;
}
});
client.onClose(function () {
if (retries === 0) {
handlers.close();
}
// Try to reconnect.
client = null;
// After 10 retries stop trying, to prevent logspam.
if (retries < maxRetries) {
// Exponentially increase timeout to reconnect.
// Respectfully copied from the package `got`.
// eslint-disable-next-line no-restricted-properties
var retryInMs = 1000 * Math.pow(2, retries) + Math.random() * 100;
retries += 1;
log.info("Trying to reconnect...");
timeout = setTimeout(function () {
socket(url, handlers, reconnect);
}, retryInMs);
}
});
client.onMessage(
/**
* @param {any} data
*/
function (data) {
var message = JSON.parse(data);
if (handlers[message.type]) {
handlers[message.type](message.data, message.params);
}
});
};
export default socket;

19
node_modules/webpack-dev-server/client/utils/log.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
import logger from "../modules/logger/index.js";
var name = "webpack-dev-server";
// default level is set on the client side, so it does not need
// to be set by the CLI or API
var defaultLevel = "info";
// options new options, merge with old options
/**
* @param {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} level
* @returns {void}
*/
function setLogLevel(level) {
logger.configureDefaultLogger({
level: level
});
}
setLogLevel(defaultLevel);
var log = logger.getLogger(name);
export { log, setLogLevel };

View File

@@ -0,0 +1,16 @@
/* global __resourceQuery WorkerGlobalScope */
// Send messages to the outside, so plugins can consume it.
/**
* @param {string} type
* @param {any} [data]
*/
function sendMsg(type, data) {
if (typeof self !== "undefined" && (typeof WorkerGlobalScope === "undefined" || !(self instanceof WorkerGlobalScope))) {
self.postMessage({
type: "webpack".concat(type),
data: data
}, "*");
}
}
export default sendMsg;

3597
node_modules/webpack-dev-server/lib/Server.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

133
node_modules/webpack-dev-server/lib/getPort.js generated vendored Normal file
View File

@@ -0,0 +1,133 @@
"use strict";
/*
* Based on the packages get-port https://www.npmjs.com/package/get-port
* and portfinder https://www.npmjs.com/package/portfinder
* The code structure is similar to get-port, but it searches
* ports deterministically like portfinder
*/
const net = require("net");
const os = require("os");
const minPort = 1024;
const maxPort = 65_535;
/**
* @return {Set<string|undefined>}
*/
const getLocalHosts = () => {
const interfaces = os.networkInterfaces();
// Add undefined value for createServer function to use default host,
// and default IPv4 host in case createServer defaults to IPv6.
// eslint-disable-next-line no-undefined
const results = new Set([undefined, "0.0.0.0"]);
for (const _interface of Object.values(interfaces)) {
if (_interface) {
for (const config of _interface) {
results.add(config.address);
}
}
}
return results;
};
/**
* @param {number} basePort
* @param {string | undefined} host
* @return {Promise<number>}
*/
const checkAvailablePort = (basePort, host) =>
new Promise((resolve, reject) => {
const server = net.createServer();
server.unref();
server.on("error", reject);
server.listen(basePort, host, () => {
// Next line should return AddressInfo because we're calling it after listen() and before close()
const { port } = /** @type {import("net").AddressInfo} */ (
server.address()
);
server.close(() => {
resolve(port);
});
});
});
/**
* @param {number} port
* @param {Set<string|undefined>} hosts
* @return {Promise<number>}
*/
const getAvailablePort = async (port, hosts) => {
/**
* Errors that mean that host is not available.
* @type {Set<string | undefined>}
*/
const nonExistentInterfaceErrors = new Set(["EADDRNOTAVAIL", "EINVAL"]);
/* Check if the post is available on every local host name */
for (const host of hosts) {
try {
await checkAvailablePort(port, host); // eslint-disable-line no-await-in-loop
} catch (error) {
/* We throw an error only if the interface exists */
if (
!nonExistentInterfaceErrors.has(
/** @type {NodeJS.ErrnoException} */ (error).code,
)
) {
throw error;
}
}
}
return port;
};
/**
* @param {number} basePort
* @param {string=} host
* @return {Promise<number>}
*/
async function getPorts(basePort, host) {
if (basePort < minPort || basePort > maxPort) {
throw new Error(`Port number must lie between ${minPort} and ${maxPort}`);
}
let port = basePort;
const localhosts = getLocalHosts();
let hosts;
if (host && !localhosts.has(host)) {
hosts = new Set([host]);
} else {
/* If the host is equivalent to localhost
we need to check every equivalent host
else the port might falsely appear as available
on some operating systems */
hosts = localhosts;
}
/** @type {Set<string | undefined>} */
const portUnavailableErrors = new Set(["EADDRINUSE", "EACCES"]);
while (port <= maxPort) {
try {
const availablePort = await getAvailablePort(port, hosts); // eslint-disable-line no-await-in-loop
return availablePort;
} catch (error) {
/* Try next port if port is busy; throw for any other error */
if (
!portUnavailableErrors.has(
/** @type {NodeJS.ErrnoException} */ (error).code,
)
) {
throw error;
}
port += 1;
}
}
throw new Error("No available ports found");
}
module.exports = getPorts;

1034
node_modules/webpack-dev-server/lib/options.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
"use strict";
/** @typedef {import("../Server").ClientConnection} ClientConnection */
// base class that users should extend if they are making their own
// server implementation
module.exports = class BaseServer {
/**
* @param {import("../Server")} server
*/
constructor(server) {
/** @type {import("../Server")} */
this.server = server;
/** @type {ClientConnection[]} */
this.clients = [];
}
};

View File

@@ -0,0 +1,126 @@
"use strict";
const sockjs = require("sockjs");
const BaseServer = require("./BaseServer");
/** @typedef {import("../Server").WebSocketServerConfiguration} WebSocketServerConfiguration */
/** @typedef {import("../Server").ClientConnection} ClientConnection */
// Workaround for sockjs@~0.3.19
// sockjs will remove Origin header, however Origin header is required for checking host.
// See https://github.com/webpack/webpack-dev-server/issues/1604 for more information
{
// @ts-ignore
const SockjsSession = require("sockjs/lib/transport").Session;
const decorateConnection = SockjsSession.prototype.decorateConnection;
/**
* @param {import("http").IncomingMessage} req
*/
// eslint-disable-next-line func-names
SockjsSession.prototype.decorateConnection = function (req) {
decorateConnection.call(this, req);
const connection = this.connection;
if (
connection.headers &&
!("origin" in connection.headers) &&
"origin" in req.headers
) {
connection.headers.origin = req.headers.origin;
}
};
}
module.exports = class SockJSServer extends BaseServer {
// options has: error (function), debug (function), server (http/s server), path (string)
/**
* @param {import("../Server")} server
*/
constructor(server) {
super(server);
const webSocketServerOptions =
/** @type {NonNullable<WebSocketServerConfiguration["options"]>} */
(
/** @type {WebSocketServerConfiguration} */
(this.server.options.webSocketServer).options
);
/**
* @param {NonNullable<WebSocketServerConfiguration["options"]>} options
* @returns {string}
*/
const getSockjsUrl = (options) => {
if (typeof options.sockjsUrl !== "undefined") {
return options.sockjsUrl;
}
return "/__webpack_dev_server__/sockjs.bundle.js";
};
this.implementation = sockjs.createServer({
// Use provided up-to-date sockjs-client
sockjs_url: getSockjsUrl(webSocketServerOptions),
// Default logger is very annoy. Limit useless logs.
/**
* @param {string} severity
* @param {string} line
*/
log: (severity, line) => {
if (severity === "error") {
this.server.logger.error(line);
} else if (severity === "info") {
this.server.logger.log(line);
} else {
this.server.logger.debug(line);
}
},
});
/**
* @param {import("sockjs").ServerOptions & { path?: string }} options
* @returns {string | undefined}
*/
const getPrefix = (options) => {
if (typeof options.prefix !== "undefined") {
return options.prefix;
}
return options.path;
};
const options = {
...webSocketServerOptions,
prefix: getPrefix(webSocketServerOptions),
};
this.implementation.installHandlers(
/** @type {import("http").Server} */ (this.server.server),
options,
);
this.implementation.on("connection", (client) => {
// @ts-ignore
// Implement the the same API as for `ws`
client.send = client.write;
// @ts-ignore
client.terminate = client.close;
this.clients.push(/** @type {ClientConnection} */ (client));
client.on("close", () => {
this.clients.splice(
this.clients.indexOf(/** @type {ClientConnection} */ (client)),
1,
);
});
});
// @ts-ignore
this.implementation.close = (callback) => {
callback();
};
}
};

View File

@@ -0,0 +1,116 @@
"use strict";
const WebSocket = require("ws");
const BaseServer = require("./BaseServer");
/** @typedef {import("../Server").WebSocketServerConfiguration} WebSocketServerConfiguration */
/** @typedef {import("../Server").ClientConnection} ClientConnection */
module.exports = class WebsocketServer extends BaseServer {
static heartbeatInterval = 1000;
/**
* @param {import("../Server")} server
*/
constructor(server) {
super(server);
/** @type {import("ws").ServerOptions} */
const options = {
.../** @type {WebSocketServerConfiguration} */
(this.server.options.webSocketServer).options,
clientTracking: false,
};
const isNoServerMode =
typeof options.port === "undefined" &&
typeof options.server === "undefined";
if (isNoServerMode) {
options.noServer = true;
}
this.implementation = new WebSocket.Server(options);
/** @type {import("http").Server} */
(this.server.server).on(
"upgrade",
/**
* @param {import("http").IncomingMessage} req
* @param {import("stream").Duplex} sock
* @param {Buffer} head
*/
(req, sock, head) => {
if (!this.implementation.shouldHandle(req)) {
return;
}
this.implementation.handleUpgrade(req, sock, head, (connection) => {
this.implementation.emit("connection", connection, req);
});
},
);
this.implementation.on(
"error",
/**
* @param {Error} err
*/
(err) => {
this.server.logger.error(err.message);
},
);
const interval = setInterval(() => {
this.clients.forEach(
/**
* @param {ClientConnection} client
*/
(client) => {
if (client.isAlive === false) {
client.terminate();
return;
}
client.isAlive = false;
client.ping(() => {});
},
);
}, WebsocketServer.heartbeatInterval);
this.implementation.on(
"connection",
/**
* @param {ClientConnection} client
*/
(client) => {
this.clients.push(client);
client.isAlive = true;
client.on("pong", () => {
client.isAlive = true;
});
client.on("close", () => {
this.clients.splice(this.clients.indexOf(client), 1);
});
// TODO: add a test case for this - https://github.com/webpack/webpack-dev-server/issues/5018
client.on(
"error",
/**
* @param {Error} err
*/
(err) => {
this.server.logger.error(err.message);
},
);
},
);
this.implementation.on("close", () => {
clearInterval(interval);
});
}
};

View File

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

View File

@@ -0,0 +1,308 @@
# Chokidar [![Weekly downloads](https://img.shields.io/npm/dw/chokidar.svg)](https://github.com/paulmillr/chokidar) [![Yearly downloads](https://img.shields.io/npm/dy/chokidar.svg)](https://github.com/paulmillr/chokidar)
> Minimal and efficient cross-platform file watching library
[![NPM](https://nodei.co/npm/chokidar.png)](https://www.npmjs.com/package/chokidar)
## Why?
Node.js `fs.watch`:
* Doesn't report filenames on MacOS.
* Doesn't report events at all when using editors like Sublime on MacOS.
* Often reports events twice.
* Emits most changes as `rename`.
* Does not provide an easy way to recursively watch file trees.
* Does not support recursive watching on Linux.
Node.js `fs.watchFile`:
* Almost as bad at event handling.
* Also does not provide any recursive watching.
* Results in high CPU utilization.
Chokidar resolves these problems.
Initially made for **[Brunch](https://brunch.io/)** (an ultra-swift web app build tool), it is now used in
[Microsoft's Visual Studio Code](https://github.com/microsoft/vscode),
[gulp](https://github.com/gulpjs/gulp/),
[karma](https://karma-runner.github.io/),
[PM2](https://github.com/Unitech/PM2),
[browserify](http://browserify.org/),
[webpack](https://webpack.github.io/),
[BrowserSync](https://www.browsersync.io/),
and [many others](https://www.npmjs.com/browse/depended/chokidar).
It has proven itself in production environments.
Version 3 is out! Check out our blog post about it: [Chokidar 3: How to save 32TB of traffic every week](https://paulmillr.com/posts/chokidar-3-save-32tb-of-traffic/)
## How?
Chokidar does still rely on the Node.js core `fs` module, but when using
`fs.watch` and `fs.watchFile` for watching, it normalizes the events it
receives, often checking for truth by getting file stats and/or dir contents.
On MacOS, chokidar by default uses a native extension exposing the Darwin
`FSEvents` API. This provides very efficient recursive watching compared with
implementations like `kqueue` available on most \*nix platforms. Chokidar still
does have to do some work to normalize the events received that way as well.
On most other platforms, the `fs.watch`-based implementation is the default, which
avoids polling and keeps CPU usage down. Be advised that chokidar will initiate
watchers recursively for everything within scope of the paths that have been
specified, so be judicious about not wasting system resources by watching much
more than needed.
## Getting started
Install with npm:
```sh
npm install chokidar
```
Then `require` and use it in your code:
```javascript
const chokidar = require('chokidar');
// One-liner for current directory
chokidar.watch('.').on('all', (event, path) => {
console.log(event, path);
});
```
## API
```javascript
// Example of a more typical implementation structure
// Initialize watcher.
const watcher = chokidar.watch('file, dir, glob, or array', {
ignored: /(^|[\/\\])\../, // ignore dotfiles
persistent: true
});
// Something to use when events are received.
const log = console.log.bind(console);
// Add event listeners.
watcher
.on('add', path => log(`File ${path} has been added`))
.on('change', path => log(`File ${path} has been changed`))
.on('unlink', path => log(`File ${path} has been removed`));
// More possible events.
watcher
.on('addDir', path => log(`Directory ${path} has been added`))
.on('unlinkDir', path => log(`Directory ${path} has been removed`))
.on('error', error => log(`Watcher error: ${error}`))
.on('ready', () => log('Initial scan complete. Ready for changes'))
.on('raw', (event, path, details) => { // internal
log('Raw event info:', event, path, details);
});
// 'add', 'addDir' and 'change' events also receive stat() results as second
// argument when available: https://nodejs.org/api/fs.html#fs_class_fs_stats
watcher.on('change', (path, stats) => {
if (stats) console.log(`File ${path} changed size to ${stats.size}`);
});
// Watch new files.
watcher.add('new-file');
watcher.add(['new-file-2', 'new-file-3', '**/other-file*']);
// Get list of actual paths being watched on the filesystem
var watchedPaths = watcher.getWatched();
// Un-watch some files.
await watcher.unwatch('new-file*');
// Stop watching.
// The method is async!
watcher.close().then(() => console.log('closed'));
// Full list of options. See below for descriptions.
// Do not use this example!
chokidar.watch('file', {
persistent: true,
ignored: '*.txt',
ignoreInitial: false,
followSymlinks: true,
cwd: '.',
disableGlobbing: false,
usePolling: false,
interval: 100,
binaryInterval: 300,
alwaysStat: false,
depth: 99,
awaitWriteFinish: {
stabilityThreshold: 2000,
pollInterval: 100
},
ignorePermissionErrors: false,
atomic: true // or a custom 'atomicity delay', in milliseconds (default 100)
});
```
`chokidar.watch(paths, [options])`
* `paths` (string or array of strings). Paths to files, dirs to be watched
recursively, or glob patterns.
- Note: globs must not contain windows separators (`\`),
because that's how they work by the standard —
you'll need to replace them with forward slashes (`/`).
- Note 2: for additional glob documentation, check out low-level
library: [picomatch](https://github.com/micromatch/picomatch).
* `options` (object) Options object as defined below:
#### Persistence
* `persistent` (default: `true`). Indicates whether the process
should continue to run as long as files are being watched. If set to
`false` when using `fsevents` to watch, no more events will be emitted
after `ready`, even if the process continues to run.
#### Path filtering
* `ignored` ([anymatch](https://github.com/es128/anymatch)-compatible definition)
Defines files/paths to be ignored. The whole relative or absolute path is
tested, not just filename. If a function with two arguments is provided, it
gets called twice per path - once with a single argument (the path), second
time with two arguments (the path and the
[`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
object of that path).
* `ignoreInitial` (default: `false`). If set to `false` then `add`/`addDir` events are also emitted for matching paths while
instantiating the watching as chokidar discovers these file paths (before the `ready` event).
* `followSymlinks` (default: `true`). When `false`, only the
symlinks themselves will be watched for changes instead of following
the link references and bubbling events through the link's path.
* `cwd` (no default). The base directory from which watch `paths` are to be
derived. Paths emitted with events will be relative to this.
* `disableGlobbing` (default: `false`). If set to `true` then the strings passed to `.watch()` and `.add()` are treated as
literal path names, even if they look like globs.
#### Performance
* `usePolling` (default: `false`).
Whether to use fs.watchFile (backed by polling), or fs.watch. If polling
leads to high CPU utilization, consider setting this to `false`. It is
typically necessary to **set this to `true` to successfully watch files over
a network**, and it may be necessary to successfully watch files in other
non-standard situations. Setting to `true` explicitly on MacOS overrides the
`useFsEvents` default. You may also set the CHOKIDAR_USEPOLLING env variable
to true (1) or false (0) in order to override this option.
* _Polling-specific settings_ (effective when `usePolling: true`)
* `interval` (default: `100`). Interval of file system polling, in milliseconds. You may also
set the CHOKIDAR_INTERVAL env variable to override this option.
* `binaryInterval` (default: `300`). Interval of file system
polling for binary files.
([see list of binary extensions](https://github.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
* `useFsEvents` (default: `true` on MacOS). Whether to use the
`fsevents` watching interface if available. When set to `true` explicitly
and `fsevents` is available this supercedes the `usePolling` setting. When
set to `false` on MacOS, `usePolling: true` becomes the default.
* `alwaysStat` (default: `false`). If relying upon the
[`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
object that may get passed with `add`, `addDir`, and `change` events, set
this to `true` to ensure it is provided even in cases where it wasn't
already available from the underlying watch events.
* `depth` (default: `undefined`). If set, limits how many levels of
subdirectories will be traversed.
* `awaitWriteFinish` (default: `false`).
By default, the `add` event will fire when a file first appears on disk, before
the entire file has been written. Furthermore, in some cases some `change`
events will be emitted while the file is being written. In some cases,
especially when watching for large files there will be a need to wait for the
write operation to finish before responding to a file creation or modification.
Setting `awaitWriteFinish` to `true` (or a truthy value) will poll file size,
holding its `add` and `change` events until the size does not change for a
configurable amount of time. The appropriate duration setting is heavily
dependent on the OS and hardware. For accurate detection this parameter should
be relatively high, making file watching much less responsive.
Use with caution.
* *`options.awaitWriteFinish` can be set to an object in order to adjust
timing params:*
* `awaitWriteFinish.stabilityThreshold` (default: 2000). Amount of time in
milliseconds for a file size to remain constant before emitting its event.
* `awaitWriteFinish.pollInterval` (default: 100). File size polling interval, in milliseconds.
#### Errors
* `ignorePermissionErrors` (default: `false`). Indicates whether to watch files
that don't have read permissions if possible. If watching fails due to `EPERM`
or `EACCES` with this set to `true`, the errors will be suppressed silently.
* `atomic` (default: `true` if `useFsEvents` and `usePolling` are `false`).
Automatically filters out artifacts that occur when using editors that use
"atomic writes" instead of writing directly to the source file. If a file is
re-added within 100 ms of being deleted, Chokidar emits a `change` event
rather than `unlink` then `add`. If the default of 100 ms does not work well
for you, you can override it by setting `atomic` to a custom value, in
milliseconds.
### Methods & Events
`chokidar.watch()` produces an instance of `FSWatcher`. Methods of `FSWatcher`:
* `.add(path / paths)`: Add files, directories, or glob patterns for tracking.
Takes an array of strings or just one string.
* `.on(event, callback)`: Listen for an FS event.
Available events: `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `ready`,
`raw`, `error`.
Additionally `all` is available which gets emitted with the underlying event
name and path for every event other than `ready`, `raw`, and `error`. `raw` is internal, use it carefully.
* `.unwatch(path / paths)`: Stop watching files, directories, or glob patterns.
Takes an array of strings or just one string.
* `.close()`: **async** Removes all listeners from watched files. Asynchronous, returns Promise. Use with `await` to ensure bugs don't happen.
* `.getWatched()`: Returns an object representing all the paths on the file
system being watched by this `FSWatcher` instance. The object's keys are all the
directories (using absolute paths unless the `cwd` option was used), and the
values are arrays of the names of the items contained in each directory.
## CLI
If you need a CLI interface for your file watching, check out
[chokidar-cli](https://github.com/open-cli-tools/chokidar-cli), allowing you to
execute a command on each change, or get a stdio stream of change events.
## Install Troubleshooting
* `npm WARN optional dep failed, continuing fsevents@n.n.n`
* This message is normal part of how `npm` handles optional dependencies and is
not indicative of a problem. Even if accompanied by other related error messages,
Chokidar should function properly.
* `TypeError: fsevents is not a constructor`
* Update chokidar by doing `rm -rf node_modules package-lock.json yarn.lock && npm install`, or update your dependency that uses chokidar.
* Chokidar is producing `ENOSP` error on Linux, like this:
* `bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell`
`Error: watch /home/ ENOSPC`
* This means Chokidar ran out of file handles and you'll need to increase their count by executing the following command in Terminal:
`echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`
## Changelog
For more detailed changelog, see [`full_changelog.md`](.github/full_changelog.md).
- **v3.5 (Jan 6, 2021):** Support for ARM Macs with Apple Silicon. Fixes for deleted symlinks.
- **v3.4 (Apr 26, 2020):** Support for directory-based symlinks. Fixes for macos file replacement.
- **v3.3 (Nov 2, 2019):** `FSWatcher#close()` method became async. That fixes IO race conditions related to close method.
- **v3.2 (Oct 1, 2019):** Improve Linux RAM usage by 50%. Race condition fixes. Windows glob fixes. Improve stability by using tight range of dependency versions.
- **v3.1 (Sep 16, 2019):** dotfiles are no longer filtered out by default. Use `ignored` option if needed. Improve initial Linux scan time by 50%.
- **v3 (Apr 30, 2019):** massive CPU & RAM consumption improvements; reduces deps / package size by a factor of 17x and bumps Node.js requirement to v8.16 and higher.
- **v2 (Dec 29, 2017):** Globs are now posix-style-only; without windows support. Tons of bugfixes.
- **v1 (Apr 7, 2015):** Glob support, symlink support, tons of bugfixes. Node 0.8+ is supported
- **v0.1 (Apr 20, 2012):** Initial release, extracted from [Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66)
## Also
Why was chokidar named this way? What's the meaning behind it?
>Chowkidar is a transliteration of a Hindi word meaning 'watchman, gatekeeper', चौकीदार. This ultimately comes from Sanskrit _ चतुष्क_ (crossway, quadrangle, consisting-of-four). This word is also used in other languages like Urdu as (چوکیدار) which is widely used in Pakistan and India.
## License
MIT (c) Paul Miller (<https://paulmillr.com>), see [LICENSE](LICENSE) file.

View File

@@ -0,0 +1,973 @@
'use strict';
const { EventEmitter } = require('events');
const fs = require('fs');
const sysPath = require('path');
const { promisify } = require('util');
const readdirp = require('readdirp');
const anymatch = require('anymatch').default;
const globParent = require('glob-parent');
const isGlob = require('is-glob');
const braces = require('braces');
const normalizePath = require('normalize-path');
const NodeFsHandler = require('./lib/nodefs-handler');
const FsEventsHandler = require('./lib/fsevents-handler');
const {
EV_ALL,
EV_READY,
EV_ADD,
EV_CHANGE,
EV_UNLINK,
EV_ADD_DIR,
EV_UNLINK_DIR,
EV_RAW,
EV_ERROR,
STR_CLOSE,
STR_END,
BACK_SLASH_RE,
DOUBLE_SLASH_RE,
SLASH_OR_BACK_SLASH_RE,
DOT_RE,
REPLACER_RE,
SLASH,
SLASH_SLASH,
BRACE_START,
BANG,
ONE_DOT,
TWO_DOTS,
GLOBSTAR,
SLASH_GLOBSTAR,
ANYMATCH_OPTS,
STRING_TYPE,
FUNCTION_TYPE,
EMPTY_STR,
EMPTY_FN,
isWindows,
isMacos,
isIBMi
} = require('./lib/constants');
const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);
/**
* @typedef {String} Path
* @typedef {'all'|'add'|'addDir'|'change'|'unlink'|'unlinkDir'|'raw'|'error'|'ready'} EventName
* @typedef {'readdir'|'watch'|'add'|'remove'|'change'} ThrottleType
*/
/**
*
* @typedef {Object} WatchHelpers
* @property {Boolean} followSymlinks
* @property {'stat'|'lstat'} statMethod
* @property {Path} path
* @property {Path} watchPath
* @property {Function} entryPath
* @property {Boolean} hasGlob
* @property {Object} globFilter
* @property {Function} filterPath
* @property {Function} filterDir
*/
const arrify = (value = []) => Array.isArray(value) ? value : [value];
const flatten = (list, result = []) => {
list.forEach(item => {
if (Array.isArray(item)) {
flatten(item, result);
} else {
result.push(item);
}
});
return result;
};
const unifyPaths = (paths_) => {
/**
* @type {Array<String>}
*/
const paths = flatten(arrify(paths_));
if (!paths.every(p => typeof p === STRING_TYPE)) {
throw new TypeError(`Non-string provided as watch path: ${paths}`);
}
return paths.map(normalizePathToUnix);
};
// If SLASH_SLASH occurs at the beginning of path, it is not replaced
// because "//StoragePC/DrivePool/Movies" is a valid network path
const toUnix = (string) => {
let str = string.replace(BACK_SLASH_RE, SLASH);
let prepend = false;
if (str.startsWith(SLASH_SLASH)) {
prepend = true;
}
while (str.match(DOUBLE_SLASH_RE)) {
str = str.replace(DOUBLE_SLASH_RE, SLASH);
}
if (prepend) {
str = SLASH + str;
}
return str;
};
// Our version of upath.normalize
// TODO: this is not equal to path-normalize module - investigate why
const normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path)));
const normalizeIgnored = (cwd = EMPTY_STR) => (path) => {
if (typeof path !== STRING_TYPE) return path;
return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path));
};
const getAbsolutePath = (path, cwd) => {
if (sysPath.isAbsolute(path)) {
return path;
}
if (path.startsWith(BANG)) {
return BANG + sysPath.join(cwd, path.slice(1));
}
return sysPath.join(cwd, path);
};
const undef = (opts, key) => opts[key] === undefined;
/**
* Directory entry.
* @property {Path} path
* @property {Set<Path>} items
*/
class DirEntry {
/**
* @param {Path} dir
* @param {Function} removeWatcher
*/
constructor(dir, removeWatcher) {
this.path = dir;
this._removeWatcher = removeWatcher;
/** @type {Set<Path>} */
this.items = new Set();
}
add(item) {
const {items} = this;
if (!items) return;
if (item !== ONE_DOT && item !== TWO_DOTS) items.add(item);
}
async remove(item) {
const {items} = this;
if (!items) return;
items.delete(item);
if (items.size > 0) return;
const dir = this.path;
try {
await readdir(dir);
} catch (err) {
if (this._removeWatcher) {
this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));
}
}
}
has(item) {
const {items} = this;
if (!items) return;
return items.has(item);
}
/**
* @returns {Array<String>}
*/
getChildren() {
const {items} = this;
if (!items) return;
return [...items.values()];
}
dispose() {
this.items.clear();
delete this.path;
delete this._removeWatcher;
delete this.items;
Object.freeze(this);
}
}
const STAT_METHOD_F = 'stat';
const STAT_METHOD_L = 'lstat';
class WatchHelper {
constructor(path, watchPath, follow, fsw) {
this.fsw = fsw;
this.path = path = path.replace(REPLACER_RE, EMPTY_STR);
this.watchPath = watchPath;
this.fullWatchPath = sysPath.resolve(watchPath);
this.hasGlob = watchPath !== path;
/** @type {object|boolean} */
if (path === EMPTY_STR) this.hasGlob = false;
this.globSymlink = this.hasGlob && follow ? undefined : false;
this.globFilter = this.hasGlob ? anymatch(path, undefined, ANYMATCH_OPTS) : false;
this.dirParts = this.getDirParts(path);
this.dirParts.forEach((parts) => {
if (parts.length > 1) parts.pop();
});
this.followSymlinks = follow;
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
}
checkGlobSymlink(entry) {
// only need to resolve once
// first entry should always have entry.parentDir === EMPTY_STR
if (this.globSymlink === undefined) {
this.globSymlink = entry.fullParentDir === this.fullWatchPath ?
false : {realPath: entry.fullParentDir, linkPath: this.fullWatchPath};
}
if (this.globSymlink) {
return entry.fullPath.replace(this.globSymlink.realPath, this.globSymlink.linkPath);
}
return entry.fullPath;
}
entryPath(entry) {
return sysPath.join(this.watchPath,
sysPath.relative(this.watchPath, this.checkGlobSymlink(entry))
);
}
filterPath(entry) {
const {stats} = entry;
if (stats && stats.isSymbolicLink()) return this.filterDir(entry);
const resolvedPath = this.entryPath(entry);
const matchesGlob = this.hasGlob && typeof this.globFilter === FUNCTION_TYPE ?
this.globFilter(resolvedPath) : true;
return matchesGlob &&
this.fsw._isntIgnored(resolvedPath, stats) &&
this.fsw._hasReadPermissions(stats);
}
getDirParts(path) {
if (!this.hasGlob) return [];
const parts = [];
const expandedPath = path.includes(BRACE_START) ? braces.expand(path) : [path];
expandedPath.forEach((path) => {
parts.push(sysPath.relative(this.watchPath, path).split(SLASH_OR_BACK_SLASH_RE));
});
return parts;
}
filterDir(entry) {
if (this.hasGlob) {
const entryParts = this.getDirParts(this.checkGlobSymlink(entry));
let globstar = false;
this.unmatchedGlob = !this.dirParts.some((parts) => {
return parts.every((part, i) => {
if (part === GLOBSTAR) globstar = true;
return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i], ANYMATCH_OPTS);
});
});
}
return !this.unmatchedGlob && this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
}
}
/**
* Watches files & directories for changes. Emitted events:
* `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
*
* new FSWatcher()
* .add(directories)
* .on('add', path => log('File', path, 'was added'))
*/
class FSWatcher extends EventEmitter {
// Not indenting methods for history sake; for now.
constructor(_opts) {
super();
const opts = {};
if (_opts) Object.assign(opts, _opts); // for frozen objects
/** @type {Map<String, DirEntry>} */
this._watched = new Map();
/** @type {Map<String, Array>} */
this._closers = new Map();
/** @type {Set<String>} */
this._ignoredPaths = new Set();
/** @type {Map<ThrottleType, Map>} */
this._throttled = new Map();
/** @type {Map<Path, String|Boolean>} */
this._symlinkPaths = new Map();
this._streams = new Set();
this.closed = false;
// Set up default options.
if (undef(opts, 'persistent')) opts.persistent = true;
if (undef(opts, 'ignoreInitial')) opts.ignoreInitial = false;
if (undef(opts, 'ignorePermissionErrors')) opts.ignorePermissionErrors = false;
if (undef(opts, 'interval')) opts.interval = 100;
if (undef(opts, 'binaryInterval')) opts.binaryInterval = 300;
if (undef(opts, 'disableGlobbing')) opts.disableGlobbing = false;
opts.enableBinaryInterval = opts.binaryInterval !== opts.interval;
// Enable fsevents on OS X when polling isn't explicitly enabled.
if (undef(opts, 'useFsEvents')) opts.useFsEvents = !opts.usePolling;
// If we can't use fsevents, ensure the options reflect it's disabled.
const canUseFsEvents = FsEventsHandler.canUse();
if (!canUseFsEvents) opts.useFsEvents = false;
// Use polling on Mac if not using fsevents.
// Other platforms use non-polling fs_watch.
if (undef(opts, 'usePolling') && !opts.useFsEvents) {
opts.usePolling = isMacos;
}
// Always default to polling on IBM i because fs.watch() is not available on IBM i.
if(isIBMi) {
opts.usePolling = true;
}
// Global override (useful for end-developers that need to force polling for all
// instances of chokidar, regardless of usage/dependency depth)
const envPoll = process.env.CHOKIDAR_USEPOLLING;
if (envPoll !== undefined) {
const envLower = envPoll.toLowerCase();
if (envLower === 'false' || envLower === '0') {
opts.usePolling = false;
} else if (envLower === 'true' || envLower === '1') {
opts.usePolling = true;
} else {
opts.usePolling = !!envLower;
}
}
const envInterval = process.env.CHOKIDAR_INTERVAL;
if (envInterval) {
opts.interval = Number.parseInt(envInterval, 10);
}
// Editor atomic write normalization enabled by default with fs.watch
if (undef(opts, 'atomic')) opts.atomic = !opts.usePolling && !opts.useFsEvents;
if (opts.atomic) this._pendingUnlinks = new Map();
if (undef(opts, 'followSymlinks')) opts.followSymlinks = true;
if (undef(opts, 'awaitWriteFinish')) opts.awaitWriteFinish = false;
if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {};
const awf = opts.awaitWriteFinish;
if (awf) {
if (!awf.stabilityThreshold) awf.stabilityThreshold = 2000;
if (!awf.pollInterval) awf.pollInterval = 100;
this._pendingWrites = new Map();
}
if (opts.ignored) opts.ignored = arrify(opts.ignored);
let readyCalls = 0;
this._emitReady = () => {
readyCalls++;
if (readyCalls >= this._readyCount) {
this._emitReady = EMPTY_FN;
this._readyEmitted = true;
// use process.nextTick to allow time for listener to be bound
process.nextTick(() => this.emit(EV_READY));
}
};
this._emitRaw = (...args) => this.emit(EV_RAW, ...args);
this._readyEmitted = false;
this.options = opts;
// Initialize with proper watcher.
if (opts.useFsEvents) {
this._fsEventsHandler = new FsEventsHandler(this);
} else {
this._nodeFsHandler = new NodeFsHandler(this);
}
// Youre frozen when your hearts not open.
Object.freeze(opts);
}
// Public methods
/**
* Adds paths to be watched on an existing FSWatcher instance
* @param {Path|Array<Path>} paths_
* @param {String=} _origAdd private; for handling non-existent paths to be watched
* @param {Boolean=} _internal private; indicates a non-user add
* @returns {FSWatcher} for chaining
*/
add(paths_, _origAdd, _internal) {
const {cwd, disableGlobbing} = this.options;
this.closed = false;
let paths = unifyPaths(paths_);
if (cwd) {
paths = paths.map((path) => {
const absPath = getAbsolutePath(path, cwd);
// Check `path` instead of `absPath` because the cwd portion can't be a glob
if (disableGlobbing || !isGlob(path)) {
return absPath;
}
return normalizePath(absPath);
});
}
// set aside negated glob strings
paths = paths.filter((path) => {
if (path.startsWith(BANG)) {
this._ignoredPaths.add(path.slice(1));
return false;
}
// if a path is being added that was previously ignored, stop ignoring it
this._ignoredPaths.delete(path);
this._ignoredPaths.delete(path + SLASH_GLOBSTAR);
// reset the cached userIgnored anymatch fn
// to make ignoredPaths changes effective
this._userIgnored = undefined;
return true;
});
if (this.options.useFsEvents && this._fsEventsHandler) {
if (!this._readyCount) this._readyCount = paths.length;
if (this.options.persistent) this._readyCount += paths.length;
paths.forEach((path) => this._fsEventsHandler._addToFsEvents(path));
} else {
if (!this._readyCount) this._readyCount = 0;
this._readyCount += paths.length;
Promise.all(
paths.map(async path => {
const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, 0, 0, _origAdd);
if (res) this._emitReady();
return res;
})
).then(results => {
if (this.closed) return;
results.filter(item => item).forEach(item => {
this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
});
});
}
return this;
}
/**
* Close watchers or start ignoring events from specified paths.
* @param {Path|Array<Path>} paths_ - string or array of strings, file/directory paths and/or globs
* @returns {FSWatcher} for chaining
*/
unwatch(paths_) {
if (this.closed) return this;
const paths = unifyPaths(paths_);
const {cwd} = this.options;
paths.forEach((path) => {
// convert to absolute path unless relative path already matches
if (!sysPath.isAbsolute(path) && !this._closers.has(path)) {
if (cwd) path = sysPath.join(cwd, path);
path = sysPath.resolve(path);
}
this._closePath(path);
this._ignoredPaths.add(path);
if (this._watched.has(path)) {
this._ignoredPaths.add(path + SLASH_GLOBSTAR);
}
// reset the cached userIgnored anymatch fn
// to make ignoredPaths changes effective
this._userIgnored = undefined;
});
return this;
}
/**
* Close watchers and remove all listeners from watched paths.
* @returns {Promise<void>}.
*/
close() {
if (this.closed) return this._closePromise;
this.closed = true;
// Memory management.
this.removeAllListeners();
const closers = [];
this._closers.forEach(closerList => closerList.forEach(closer => {
const promise = closer();
if (promise instanceof Promise) closers.push(promise);
}));
this._streams.forEach(stream => stream.destroy());
this._userIgnored = undefined;
this._readyCount = 0;
this._readyEmitted = false;
this._watched.forEach(dirent => dirent.dispose());
['closers', 'watched', 'streams', 'symlinkPaths', 'throttled'].forEach(key => {
this[`_${key}`].clear();
});
this._closePromise = closers.length ? Promise.all(closers).then(() => undefined) : Promise.resolve();
return this._closePromise;
}
/**
* Expose list of watched paths
* @returns {Object} for chaining
*/
getWatched() {
const watchList = {};
this._watched.forEach((entry, dir) => {
const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;
watchList[key || ONE_DOT] = entry.getChildren().sort();
});
return watchList;
}
emitWithAll(event, args) {
this.emit(...args);
if (event !== EV_ERROR) this.emit(EV_ALL, ...args);
}
// Common helpers
// --------------
/**
* Normalize and emit events.
* Calling _emit DOES NOT MEAN emit() would be called!
* @param {EventName} event Type of event
* @param {Path} path File or directory path
* @param {*=} val1 arguments to be passed with event
* @param {*=} val2
* @param {*=} val3
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
async _emit(event, path, val1, val2, val3) {
if (this.closed) return;
const opts = this.options;
if (isWindows) path = sysPath.normalize(path);
if (opts.cwd) path = sysPath.relative(opts.cwd, path);
/** @type Array<any> */
const args = [event, path];
if (val3 !== undefined) args.push(val1, val2, val3);
else if (val2 !== undefined) args.push(val1, val2);
else if (val1 !== undefined) args.push(val1);
const awf = opts.awaitWriteFinish;
let pw;
if (awf && (pw = this._pendingWrites.get(path))) {
pw.lastChange = new Date();
return this;
}
if (opts.atomic) {
if (event === EV_UNLINK) {
this._pendingUnlinks.set(path, args);
setTimeout(() => {
this._pendingUnlinks.forEach((entry, path) => {
this.emit(...entry);
this.emit(EV_ALL, ...entry);
this._pendingUnlinks.delete(path);
});
}, typeof opts.atomic === 'number' ? opts.atomic : 100);
return this;
}
if (event === EV_ADD && this._pendingUnlinks.has(path)) {
event = args[0] = EV_CHANGE;
this._pendingUnlinks.delete(path);
}
}
if (awf && (event === EV_ADD || event === EV_CHANGE) && this._readyEmitted) {
const awfEmit = (err, stats) => {
if (err) {
event = args[0] = EV_ERROR;
args[1] = err;
this.emitWithAll(event, args);
} else if (stats) {
// if stats doesn't exist the file must have been deleted
if (args.length > 2) {
args[2] = stats;
} else {
args.push(stats);
}
this.emitWithAll(event, args);
}
};
this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
return this;
}
if (event === EV_CHANGE) {
const isThrottled = !this._throttle(EV_CHANGE, path, 50);
if (isThrottled) return this;
}
if (opts.alwaysStat && val1 === undefined &&
(event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE)
) {
const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;
let stats;
try {
stats = await stat(fullPath);
} catch (err) {}
// Suppress event when fs_stat fails, to avoid sending undefined 'stat'
if (!stats || this.closed) return;
args.push(stats);
}
this.emitWithAll(event, args);
return this;
}
/**
* Common handler for errors
* @param {Error} error
* @returns {Error|Boolean} The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
_handleError(error) {
const code = error && error.code;
if (error && code !== 'ENOENT' && code !== 'ENOTDIR' &&
(!this.options.ignorePermissionErrors || (code !== 'EPERM' && code !== 'EACCES'))
) {
this.emit(EV_ERROR, error);
}
return error || this.closed;
}
/**
* Helper utility for throttling
* @param {ThrottleType} actionType type being throttled
* @param {Path} path being acted upon
* @param {Number} timeout duration of time to suppress duplicate actions
* @returns {Object|false} tracking object or false if action should be suppressed
*/
_throttle(actionType, path, timeout) {
if (!this._throttled.has(actionType)) {
this._throttled.set(actionType, new Map());
}
/** @type {Map<Path, Object>} */
const action = this._throttled.get(actionType);
/** @type {Object} */
const actionPath = action.get(path);
if (actionPath) {
actionPath.count++;
return false;
}
let timeoutObject;
const clear = () => {
const item = action.get(path);
const count = item ? item.count : 0;
action.delete(path);
clearTimeout(timeoutObject);
if (item) clearTimeout(item.timeoutObject);
return count;
};
timeoutObject = setTimeout(clear, timeout);
const thr = {timeoutObject, clear, count: 0};
action.set(path, thr);
return thr;
}
_incrReadyCount() {
return this._readyCount++;
}
/**
* Awaits write operation to finish.
* Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
* @param {Path} path being acted upon
* @param {Number} threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
* @param {EventName} event
* @param {Function} awfEmit Callback to be called when ready for event to be emitted.
*/
_awaitWriteFinish(path, threshold, event, awfEmit) {
let timeoutHandler;
let fullPath = path;
if (this.options.cwd && !sysPath.isAbsolute(path)) {
fullPath = sysPath.join(this.options.cwd, path);
}
const now = new Date();
const awaitWriteFinish = (prevStat) => {
fs.stat(fullPath, (err, curStat) => {
if (err || !this._pendingWrites.has(path)) {
if (err && err.code !== 'ENOENT') awfEmit(err);
return;
}
const now = Number(new Date());
if (prevStat && curStat.size !== prevStat.size) {
this._pendingWrites.get(path).lastChange = now;
}
const pw = this._pendingWrites.get(path);
const df = now - pw.lastChange;
if (df >= threshold) {
this._pendingWrites.delete(path);
awfEmit(undefined, curStat);
} else {
timeoutHandler = setTimeout(
awaitWriteFinish,
this.options.awaitWriteFinish.pollInterval,
curStat
);
}
});
};
if (!this._pendingWrites.has(path)) {
this._pendingWrites.set(path, {
lastChange: now,
cancelWait: () => {
this._pendingWrites.delete(path);
clearTimeout(timeoutHandler);
return event;
}
});
timeoutHandler = setTimeout(
awaitWriteFinish,
this.options.awaitWriteFinish.pollInterval
);
}
}
_getGlobIgnored() {
return [...this._ignoredPaths.values()];
}
/**
* Determines whether user has asked to ignore this path.
* @param {Path} path filepath or dir
* @param {fs.Stats=} stats result of fs.stat
* @returns {Boolean}
*/
_isIgnored(path, stats) {
if (this.options.atomic && DOT_RE.test(path)) return true;
if (!this._userIgnored) {
const {cwd} = this.options;
const ign = this.options.ignored;
const ignored = ign && ign.map(normalizeIgnored(cwd));
const paths = arrify(ignored)
.filter((path) => typeof path === STRING_TYPE && !isGlob(path))
.map((path) => path + SLASH_GLOBSTAR);
const list = this._getGlobIgnored().map(normalizeIgnored(cwd)).concat(ignored, paths);
this._userIgnored = anymatch(list, undefined, ANYMATCH_OPTS);
}
return this._userIgnored([path, stats]);
}
_isntIgnored(path, stat) {
return !this._isIgnored(path, stat);
}
/**
* Provides a set of common helpers and properties relating to symlink and glob handling.
* @param {Path} path file, directory, or glob pattern being watched
* @param {Number=} depth at any depth > 0, this isn't a glob
* @returns {WatchHelper} object containing helpers for this path
*/
_getWatchHelpers(path, depth) {
const watchPath = depth || this.options.disableGlobbing || !isGlob(path) ? path : globParent(path);
const follow = this.options.followSymlinks;
return new WatchHelper(path, watchPath, follow, this);
}
// Directory helpers
// -----------------
/**
* Provides directory tracking objects
* @param {String} directory path of the directory
* @returns {DirEntry} the directory's tracking object
*/
_getWatchedDir(directory) {
if (!this._boundRemove) this._boundRemove = this._remove.bind(this);
const dir = sysPath.resolve(directory);
if (!this._watched.has(dir)) this._watched.set(dir, new DirEntry(dir, this._boundRemove));
return this._watched.get(dir);
}
// File helpers
// ------------
/**
* Check for read permissions.
* Based on this answer on SO: https://stackoverflow.com/a/11781404/1358405
* @param {fs.Stats} stats - object, result of fs_stat
* @returns {Boolean} indicates whether the file can be read
*/
_hasReadPermissions(stats) {
if (this.options.ignorePermissionErrors) return true;
// stats.mode may be bigint
const md = stats && Number.parseInt(stats.mode, 10);
const st = md & 0o777;
const it = Number.parseInt(st.toString(8)[0], 10);
return Boolean(4 & it);
}
/**
* Handles emitting unlink events for
* files and directories, and via recursion, for
* files and directories within directories that are unlinked
* @param {String} directory within which the following item is located
* @param {String} item base path of item/directory
* @returns {void}
*/
_remove(directory, item, isDirectory) {
// if what is being deleted is a directory, get that directory's paths
// for recursive deleting and cleaning of watched object
// if it is not a directory, nestedDirectoryChildren will be empty array
const path = sysPath.join(directory, item);
const fullPath = sysPath.resolve(path);
isDirectory = isDirectory != null
? isDirectory
: this._watched.has(path) || this._watched.has(fullPath);
// prevent duplicate handling in case of arriving here nearly simultaneously
// via multiple paths (such as _handleFile and _handleDir)
if (!this._throttle('remove', path, 100)) return;
// if the only watched file is removed, watch for its return
if (!isDirectory && !this.options.useFsEvents && this._watched.size === 1) {
this.add(directory, item, true);
}
// This will create a new entry in the watched object in either case
// so we got to do the directory check beforehand
const wp = this._getWatchedDir(path);
const nestedDirectoryChildren = wp.getChildren();
// Recursively remove children directories / files.
nestedDirectoryChildren.forEach(nested => this._remove(path, nested));
// Check if item was on the watched list and remove it
const parent = this._getWatchedDir(directory);
const wasTracked = parent.has(item);
parent.remove(item);
// Fixes issue #1042 -> Relative paths were detected and added as symlinks
// (https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L612),
// but never removed from the map in case the path was deleted.
// This leads to an incorrect state if the path was recreated:
// https://github.com/paulmillr/chokidar/blob/e1753ddbc9571bdc33b4a4af172d52cb6e611c10/lib/nodefs-handler.js#L553
if (this._symlinkPaths.has(fullPath)) {
this._symlinkPaths.delete(fullPath);
}
// If we wait for this file to be fully written, cancel the wait.
let relPath = path;
if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path);
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
const event = this._pendingWrites.get(relPath).cancelWait();
if (event === EV_ADD) return;
}
// The Entry will either be a directory that just got removed
// or a bogus entry to a file, in either case we have to remove it
this._watched.delete(path);
this._watched.delete(fullPath);
const eventName = isDirectory ? EV_UNLINK_DIR : EV_UNLINK;
if (wasTracked && !this._isIgnored(path)) this._emit(eventName, path);
// Avoid conflicts if we later create another file with the same name
if (!this.options.useFsEvents) {
this._closePath(path);
}
}
/**
* Closes all watchers for a path
* @param {Path} path
*/
_closePath(path) {
this._closeFile(path)
const dir = sysPath.dirname(path);
this._getWatchedDir(dir).remove(sysPath.basename(path));
}
/**
* Closes only file-specific watchers
* @param {Path} path
*/
_closeFile(path) {
const closers = this._closers.get(path);
if (!closers) return;
closers.forEach(closer => closer());
this._closers.delete(path);
}
/**
*
* @param {Path} path
* @param {Function} closer
*/
_addPathCloser(path, closer) {
if (!closer) return;
let list = this._closers.get(path);
if (!list) {
list = [];
this._closers.set(path, list);
}
list.push(closer);
}
_readdirp(root, opts) {
if (this.closed) return;
const options = {type: EV_ALL, alwaysStat: true, lstat: true, ...opts};
let stream = readdirp(root, options);
this._streams.add(stream);
stream.once(STR_CLOSE, () => {
stream = undefined;
});
stream.once(STR_END, () => {
if (stream) {
this._streams.delete(stream);
stream = undefined;
}
});
return stream;
}
}
// Export FSWatcher class
exports.FSWatcher = FSWatcher;
/**
* Instantiates watcher with paths to be tracked.
* @param {String|Array<String>} paths file/directory paths and/or globs
* @param {Object=} options chokidar opts
* @returns an instance of FSWatcher for chaining.
*/
const watch = (paths, options) => {
const watcher = new FSWatcher(options);
watcher.add(paths);
return watcher;
};
exports.watch = watch;

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,70 @@
{
"name": "chokidar",
"description": "Minimal and efficient cross-platform file watching library",
"version": "3.6.0",
"homepage": "https://github.com/paulmillr/chokidar",
"author": "Paul Miller (https://paulmillr.com)",
"contributors": [
"Paul Miller (https://paulmillr.com)",
"Elan Shanker"
],
"engines": {
"node": ">= 8.10.0"
},
"main": "index.js",
"types": "./types/index.d.ts",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"devDependencies": {
"@types/node": "^14",
"chai": "^4.3",
"dtslint": "^3.3.0",
"eslint": "^7.0.0",
"mocha": "^7.0.0",
"rimraf": "^3.0.0",
"sinon": "^9.0.1",
"sinon-chai": "^3.3.0",
"typescript": "^4.4.3",
"upath": "^1.2.0"
},
"files": [
"index.js",
"lib/*.js",
"types/index.d.ts"
],
"repository": {
"type": "git",
"url": "git+https://github.com/paulmillr/chokidar.git"
},
"bugs": {
"url": "https://github.com/paulmillr/chokidar/issues"
},
"license": "MIT",
"scripts": {
"dtslint": "dtslint types",
"lint": "eslint --report-unused-disable-directives --ignore-path .gitignore .",
"build": "npm ls",
"mocha": "mocha --exit --timeout 90000",
"test": "npm run lint && npm run mocha"
},
"keywords": [
"fs",
"watch",
"watchFile",
"watcher",
"watching",
"file",
"fsevents"
],
"funding": "https://paulmillr.com/funding/"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,598 @@
# http-proxy-middleware
[![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/chimurai/http-proxy-middleware/CI/master?style=flat-square)](https://github.com/chimurai/http-proxy-middleware/actions?query=branch%3Amaster)
[![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square)](https://coveralls.io/r/chimurai/http-proxy-middleware)
[![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg?style=flat-square)](https://snyk.io/test/npm/http-proxy-middleware)
[![npm](https://img.shields.io/npm/v/http-proxy-middleware?color=%23CC3534&style=flat-square)](https://www.npmjs.com/package/http-proxy-middleware)
Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/strongloop/express), [browser-sync](https://github.com/BrowserSync/browser-sync) and [many more](#compatible-servers).
Powered by the popular Nodejitsu [`http-proxy`](https://github.com/nodejitsu/node-http-proxy). [![GitHub stars](https://img.shields.io/github/stars/nodejitsu/node-http-proxy.svg?style=social&label=Star)](https://github.com/nodejitsu/node-http-proxy)
## ⚠️ Note <!-- omit in toc -->
This page is showing documentation for version v2.x.x ([release notes](https://github.com/chimurai/http-proxy-middleware/releases))
If you're looking for v0.x documentation. Go to:
https://github.com/chimurai/http-proxy-middleware/tree/v0.21.0#readme
## TL;DR <!-- omit in toc -->
Proxy `/api` requests to `http://www.example.org`
```javascript
// javascript
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
```
```typescript
// typescript
import * as express from 'express';
import { createProxyMiddleware, Filter, Options, RequestHandler } from 'http-proxy-middleware';
const app = express();
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
```
_All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#options) can be used, along with some extra `http-proxy-middleware` [options](#options).
:bulb: **Tip:** Set the option `changeOrigin` to `true` for [name-based virtual hosted sites](http://en.wikipedia.org/wiki/Virtual_hosting#Name-based).
## Table of Contents <!-- omit in toc -->
- [Install](#install)
- [Core concept](#core-concept)
- [Example](#example)
- [Context matching](#context-matching)
- [Options](#options)
- [http-proxy-middleware options](#http-proxy-middleware-options)
- [http-proxy events](#http-proxy-events)
- [http-proxy options](#http-proxy-options)
- [Shorthand](#shorthand)
- [app.use(path, proxy)](#appusepath-proxy)
- [WebSocket](#websocket)
- [External WebSocket upgrade](#external-websocket-upgrade)
- [Intercept and manipulate requests](#intercept-and-manipulate-requests)
- [Intercept and manipulate responses](#intercept-and-manipulate-responses)
- [Working examples](#working-examples)
- [Recipes](#recipes)
- [Compatible servers](#compatible-servers)
- [Tests](#tests)
- [Changelog](#changelog)
- [License](#license)
## Install
```bash
$ npm install --save-dev http-proxy-middleware
```
## Core concept
Proxy middleware configuration.
#### createProxyMiddleware([context,] config)
```javascript
const { createProxyMiddleware } = require('http-proxy-middleware');
const apiProxy = createProxyMiddleware('/api', { target: 'http://www.example.org' });
// \____/ \_____________________________/
// | |
// context options
// 'apiProxy' is now ready to be used as middleware in a server.
```
- **context**: Determine which requests should be proxied to the target host.
(more on [context matching](#context-matching))
- **options.target**: target host to proxy to. _(protocol + host)_
(full list of [`http-proxy-middleware` configuration options](#options))
#### createProxyMiddleware(uri [, config])
```javascript
// shorthand syntax for the example above:
const apiProxy = createProxyMiddleware('http://www.example.org/api');
```
More about the [shorthand configuration](#shorthand).
## Example
An example with `express` server.
```javascript
// include dependencies
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
// proxy middleware options
/** @type {import('http-proxy-middleware/dist/types').Options} */
const options = {
target: 'http://www.example.org', // target host
changeOrigin: true, // needed for virtual hosted sites
ws: true, // proxy websockets
pathRewrite: {
'^/api/old-path': '/api/new-path', // rewrite path
'^/api/remove/path': '/path', // remove base path
},
router: {
// when request.headers.host == 'dev.localhost:3000',
// override target 'http://www.example.org' to 'http://localhost:8000'
'dev.localhost:3000': 'http://localhost:8000',
},
};
// create the proxy (without context)
const exampleProxy = createProxyMiddleware(options);
// mount `exampleProxy` in web server
const app = express();
app.use('/api', exampleProxy);
app.listen(3000);
```
## Context matching
Providing an alternative way to decide which requests should be proxied; In case you are not able to use the server's [`path` parameter](http://expressjs.com/en/4x/api.html#app.use) to mount the proxy or when you need more flexibility.
[RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used for context matching.
```ascii
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
```
- **path matching**
- `createProxyMiddleware({...})` - matches any path, all requests will be proxied.
- `createProxyMiddleware('/', {...})` - matches any path, all requests will be proxied.
- `createProxyMiddleware('/api', {...})` - matches paths starting with `/api`
- **multiple path matching**
- `createProxyMiddleware(['/api', '/ajax', '/someotherpath'], {...})`
- **wildcard path matching**
For fine-grained control you can use wildcard matching. Glob pattern matching is done by _micromatch_. Visit [micromatch](https://www.npmjs.com/package/micromatch) or [glob](https://www.npmjs.com/package/glob) for more globbing examples.
- `createProxyMiddleware('**', {...})` matches any path, all requests will be proxied.
- `createProxyMiddleware('**/*.html', {...})` matches any path which ends with `.html`
- `createProxyMiddleware('/*.html', {...})` matches paths directly under path-absolute
- `createProxyMiddleware('/api/**/*.html', {...})` matches requests ending with `.html` in the path of `/api`
- `createProxyMiddleware(['/api/**', '/ajax/**'], {...})` combine multiple patterns
- `createProxyMiddleware(['/api/**', '!**/bad.json'], {...})` exclusion
**Note**: In multiple path matching, you cannot use string paths and wildcard paths together.
- **custom matching**
For full control you can provide a custom function to determine which requests should be proxied or not.
```javascript
/**
* @return {Boolean}
*/
const filter = function (pathname, req) {
return pathname.match('^/api') && req.method === 'GET';
};
const apiProxy = createProxyMiddleware(filter, {
target: 'http://www.example.org',
});
```
## Options
### http-proxy-middleware options
- **option.pathRewrite**: object/function, rewrite target's url path. Object-keys will be used as _RegExp_ to match paths.
```javascript
// rewrite path
pathRewrite: {'^/old/api' : '/new/api'}
// remove path
pathRewrite: {'^/remove/api' : ''}
// add base path
pathRewrite: {'^/' : '/basepath/'}
// custom rewriting
pathRewrite: function (path, req) { return path.replace('/api', '/base/api') }
// custom rewriting, returning Promise
pathRewrite: async function (path, req) {
const should_add_something = await httpRequestToDecideSomething(path);
if (should_add_something) path += "something";
return path;
}
```
- **option.router**: object/function, re-target `option.target` for specific requests.
```javascript
// Use `host` and/or `path` to match requests. First match will be used.
// The order of the configuration matters.
router: {
'integration.localhost:3000' : 'http://localhost:8001', // host only
'staging.localhost:3000' : 'http://localhost:8002', // host only
'localhost:3000/api' : 'http://localhost:8003', // host + path
'/rest' : 'http://localhost:8004' // path only
}
// Custom router function (string target)
router: function(req) {
return 'http://localhost:8004';
}
// Custom router function (target object)
router: function(req) {
return {
protocol: 'https:', // The : is required
host: 'localhost',
port: 8004
};
}
// Asynchronous router function which returns promise
router: async function(req) {
const url = await doSomeIO();
return url;
}
```
- **option.logLevel**: string, ['debug', 'info', 'warn', 'error', 'silent']. Default: `'info'`
- **option.logProvider**: function, modify or replace log provider. Default: `console`.
```javascript
// simple replace
function logProvider(provider) {
// replace the default console log provider.
return require('winston');
}
```
```javascript
// verbose replacement
function logProvider(provider) {
const logger = new (require('winston').Logger)();
const myCustomProvider = {
log: logger.log,
debug: logger.debug,
info: logger.info,
warn: logger.warn,
error: logger.error,
};
return myCustomProvider;
}
```
### http-proxy events
Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events):
- **option.onError**: function, subscribe to http-proxy's `error` event for custom error handling.
```javascript
function onError(err, req, res, target) {
res.writeHead(500, {
'Content-Type': 'text/plain',
});
res.end('Something went wrong. And we are reporting a custom error message.');
}
```
- **option.onProxyRes**: function, subscribe to http-proxy's `proxyRes` event.
```javascript
function onProxyRes(proxyRes, req, res) {
proxyRes.headers['x-added'] = 'foobar'; // add new header to response
delete proxyRes.headers['x-removed']; // remove header from response
}
```
- **option.onProxyReq**: function, subscribe to http-proxy's `proxyReq` event.
```javascript
function onProxyReq(proxyReq, req, res) {
// add custom header to request
proxyReq.setHeader('x-added', 'foobar');
// or log the req
}
```
- **option.onProxyReqWs**: function, subscribe to http-proxy's `proxyReqWs` event.
```javascript
function onProxyReqWs(proxyReq, req, socket, options, head) {
// add custom header
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
}
```
- **option.onOpen**: function, subscribe to http-proxy's `open` event.
```javascript
function onOpen(proxySocket) {
// listen for messages coming FROM the target here
proxySocket.on('data', hybridParseAndLogMessage);
}
```
- **option.onClose**: function, subscribe to http-proxy's `close` event.
```javascript
function onClose(res, socket, head) {
// view disconnected websocket connections
console.log('Client disconnected');
}
```
### http-proxy options
The following options are provided by the underlying [http-proxy](https://github.com/nodejitsu/node-http-proxy#options) library.
- **option.target**: url string to be parsed with the url module
- **option.forward**: url string to be parsed with the url module
- **option.agent**: object to be passed to http(s).request (see Node's [https agent](http://nodejs.org/api/https.html#https_class_https_agent) and [http agent](http://nodejs.org/api/http.html#http_class_http_agent) objects)
- **option.ssl**: object to be passed to https.createServer()
- **option.ws**: true/false: if you want to proxy websockets
- **option.xfwd**: true/false, adds x-forward headers
- **option.secure**: true/false, if you want to verify the SSL Certs
- **option.toProxy**: true/false, passes the absolute URL as the `path` (useful for proxying to proxies)
- **option.prependPath**: true/false, Default: true - specify whether you want to prepend the target's path to the proxy path
- **option.ignorePath**: true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request (note: you will have to append / manually if required).
- **option.localAddress** : Local interface string to bind for outgoing connections
- **option.changeOrigin**: true/false, Default: false - changes the origin of the host header to the target URL
- **option.preserveHeaderKeyCase**: true/false, Default: false - specify whether you want to keep letter case of response header key
- **option.auth** : Basic authentication i.e. 'user:password' to compute an Authorization header.
- **option.hostRewrite**: rewrites the location hostname on (301/302/307/308) redirects.
- **option.autoRewrite**: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false.
- **option.protocolRewrite**: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
- **option.cookieDomainRewrite**: rewrites domain of `set-cookie` headers. Possible values:
- `false` (default): disable cookie rewriting
- String: new domain, for example `cookieDomainRewrite: "new.domain"`. To remove the domain, use `cookieDomainRewrite: ""`.
- Object: mapping of domains to new domains, use `"*"` to match all domains.
For example keep one domain unchanged, rewrite one domain and remove other domains:
```json
cookieDomainRewrite: {
"unchanged.domain": "unchanged.domain",
"old.domain": "new.domain",
"*": ""
}
```
- **option.cookiePathRewrite**: rewrites path of `set-cookie` headers. Possible values:
- `false` (default): disable cookie rewriting
- String: new path, for example `cookiePathRewrite: "/newPath/"`. To remove the path, use `cookiePathRewrite: ""`. To set path to root use `cookiePathRewrite: "/"`.
- Object: mapping of paths to new paths, use `"*"` to match all paths.
For example, to keep one path unchanged, rewrite one path and remove other paths:
```json
cookiePathRewrite: {
"/unchanged.path/": "/unchanged.path/",
"/old.path/": "/new.path/",
"*": ""
}
```
- **option.headers**: object, adds [request headers](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields). (Example: `{host:'www.example.org'}`)
- **option.proxyTimeout**: timeout (in millis) when proxy receives no response from target
- **option.timeout**: timeout (in millis) for incoming requests
- **option.followRedirects**: true/false, Default: false - specify whether you want to follow redirects
- **option.selfHandleResponse** true/false, if set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the `proxyRes` event
- **option.buffer**: stream of data to send as the request body. Maybe you have some middleware that consumes the request stream before proxying it on e.g. If you read the body of a request into a field called 'req.rawbody' you could restream this field in the buffer option:
```javascript
'use strict';
const streamify = require('stream-array');
const HttpProxy = require('http-proxy');
const proxy = new HttpProxy();
module.exports = (req, res, next) => {
proxy.web(
req,
res,
{
target: 'http://localhost:4003/',
buffer: streamify(req.rawBody),
},
next
);
};
```
## Shorthand
Use the shorthand syntax when verbose configuration is not needed. The `context` and `option.target` will be automatically configured when shorthand is used. Options can still be used if needed.
```javascript
createProxyMiddleware('http://www.example.org:8000/api');
// createProxyMiddleware('/api', {target: 'http://www.example.org:8000'});
createProxyMiddleware('http://www.example.org:8000/api/books/*/**.json');
// createProxyMiddleware('/api/books/*/**.json', {target: 'http://www.example.org:8000'});
createProxyMiddleware('http://www.example.org:8000/api', { changeOrigin: true });
// createProxyMiddleware('/api', {target: 'http://www.example.org:8000', changeOrigin: true});
```
### app.use(path, proxy)
If you want to use the server's `app.use` `path` parameter to match requests;
Create and mount the proxy without the http-proxy-middleware `context` parameter:
```javascript
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
```
`app.use` documentation:
- express: http://expressjs.com/en/4x/api.html#app.use
- connect: https://github.com/senchalabs/connect#mount-middleware
- polka: https://github.com/lukeed/polka#usebase-fn
## WebSocket
```javascript
// verbose api
createProxyMiddleware('/', { target: 'http://echo.websocket.org', ws: true });
// shorthand
createProxyMiddleware('http://echo.websocket.org', { ws: true });
// shorter shorthand
createProxyMiddleware('ws://echo.websocket.org');
```
### External WebSocket upgrade
In the previous WebSocket examples, http-proxy-middleware relies on a initial http request in order to listen to the http `upgrade` event. If you need to proxy WebSockets without the initial http request, you can subscribe to the server's http `upgrade` event manually.
```javascript
const wsProxy = createProxyMiddleware('ws://echo.websocket.org', { changeOrigin: true });
const app = express();
app.use(wsProxy);
const server = app.listen(3000);
server.on('upgrade', wsProxy.upgrade); // <-- subscribe to http 'upgrade'
```
## Intercept and manipulate requests
Intercept requests from downstream by defining `onProxyReq` in `createProxyMiddleware`.
Currently the only pre-provided request interceptor is `fixRequestBody`, which is used to fix proxied POST requests when `bodyParser` is applied before this middleware.
Example:
```javascript
const { createProxyMiddleware, fixRequestBody } = require('http-proxy-middleware');
const proxy = createProxyMiddleware({
/**
* Fix bodyParser
**/
onProxyReq: fixRequestBody,
});
```
## Intercept and manipulate responses
Intercept responses from upstream with `responseInterceptor`. (Make sure to set `selfHandleResponse: true`)
Responses which are compressed with `brotli`, `gzip` and `deflate` will be decompressed automatically. The response will be returned as `buffer` ([docs](https://nodejs.org/api/buffer.html)) which you can manipulate.
With `buffer`, response manipulation is not limited to text responses (html/css/js, etc...); image manipulation will be possible too. ([example](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#manipulate-image-response))
NOTE: `responseInterceptor` disables streaming of target's response.
Example:
```javascript
const { createProxyMiddleware, responseInterceptor } = require('http-proxy-middleware');
const proxy = createProxyMiddleware({
/**
* IMPORTANT: avoid res.end being called automatically
**/
selfHandleResponse: true, // res.end() will be called internally by responseInterceptor()
/**
* Intercept response and replace 'Hello' with 'Goodbye'
**/
onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
const response = responseBuffer.toString('utf8'); // convert buffer to string
return response.replace('Hello', 'Goodbye'); // manipulate response and return the result
}),
});
```
Check out [interception recipes](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#readme) for more examples.
## Working examples
View and play around with [working examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples).
- Browser-Sync ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/browser-sync/index.js))
- express ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/express/index.js))
- connect ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/connect/index.js))
- WebSocket ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/websocket/index.js))
- Response Manipulation ([example source](https://github.com/chimurai/http-proxy-middleware/blob/master/examples/response-interceptor/index.js))
## Recipes
View the [recipes](https://github.com/chimurai/http-proxy-middleware/tree/master/recipes) for common use cases.
## Compatible servers
`http-proxy-middleware` is compatible with the following servers:
- [connect](https://www.npmjs.com/package/connect)
- [express](https://www.npmjs.com/package/express)
- [fastify](https://www.npmjs.com/package/fastify)
- [browser-sync](https://www.npmjs.com/package/browser-sync)
- [lite-server](https://www.npmjs.com/package/lite-server)
- [polka](https://github.com/lukeed/polka)
- [grunt-contrib-connect](https://www.npmjs.com/package/grunt-contrib-connect)
- [grunt-browser-sync](https://www.npmjs.com/package/grunt-browser-sync)
- [gulp-connect](https://www.npmjs.com/package/gulp-connect)
- [gulp-webserver](https://www.npmjs.com/package/gulp-webserver)
Sample implementations can be found in the [server recipes](https://github.com/chimurai/http-proxy-middleware/tree/master/recipes/servers.md).
## Tests
Run the test suite:
```bash
# install dependencies
$ yarn
# linting
$ yarn lint
$ yarn lint:fix
# building (compile typescript to js)
$ yarn build
# unit tests
$ yarn test
# code coverage
$ yarn cover
# check spelling mistakes
$ yarn spellcheck
```
## Changelog
- [View changelog](https://github.com/chimurai/http-proxy-middleware/blob/master/CHANGELOG.md)
## License
The MIT License (MIT)
Copyright (c) 2015-2025 Steven Chim

View File

@@ -0,0 +1,4 @@
import type { Options } from './types';
import type * as httpProxy from 'http-proxy';
export declare function init(proxy: httpProxy, option: Options): void;
export declare function getHandlers(options: Options): any;

View File

@@ -0,0 +1,84 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getHandlers = exports.init = void 0;
const logger_1 = require("./logger");
const logger = (0, logger_1.getInstance)();
function init(proxy, option) {
const handlers = getHandlers(option);
for (const eventName of Object.keys(handlers)) {
proxy.on(eventName, handlers[eventName]);
}
// https://github.com/webpack/webpack-dev-server/issues/1642
proxy.on('econnreset', (error, req, res, target) => {
logger.error(`[HPM] ECONNRESET: %O`, error);
});
// https://github.com/webpack/webpack-dev-server/issues/1642#issuecomment-1104325120
proxy.on('proxyReqWs', (proxyReq, req, socket, options, head) => {
socket.on('error', (error) => {
logger.error(`[HPM] WebSocket error: %O`, error);
});
});
logger.debug('[HPM] Subscribed to http-proxy events:', Object.keys(handlers));
}
exports.init = init;
function getHandlers(options) {
// https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events
const proxyEventsMap = {
error: 'onError',
proxyReq: 'onProxyReq',
proxyReqWs: 'onProxyReqWs',
proxyRes: 'onProxyRes',
open: 'onOpen',
close: 'onClose',
};
const handlers = {};
for (const [eventName, onEventName] of Object.entries(proxyEventsMap)) {
// all handlers for the http-proxy events are prefixed with 'on'.
// loop through options and try to find these handlers
// and add them to the handlers object for subscription in init().
const fnHandler = options ? options[onEventName] : null;
if (typeof fnHandler === 'function') {
handlers[eventName] = fnHandler;
}
}
// add default error handler in absence of error handler
if (typeof handlers.error !== 'function') {
handlers.error = defaultErrorHandler;
}
// add default close handler in absence of close handler
if (typeof handlers.close !== 'function') {
handlers.close = logClose;
}
return handlers;
}
exports.getHandlers = getHandlers;
function defaultErrorHandler(err, req, res) {
// Re-throw error. Not recoverable since req & res are empty.
if (!req && !res) {
throw err; // "Error: Must provide a proper URL as target"
}
const host = req.headers && req.headers.host;
const code = err.code;
if (res.writeHead && !res.headersSent) {
if (/HPE_INVALID/.test(code)) {
res.writeHead(502);
}
else {
switch (code) {
case 'ECONNRESET':
case 'ENOTFOUND':
case 'ECONNREFUSED':
case 'ETIMEDOUT':
res.writeHead(504);
break;
default:
res.writeHead(500);
}
}
}
res.end(`Error occurred while trying to proxy: ${host}${req.url}`);
}
function logClose(req, socket, head) {
// view disconnected websocket connections
logger.info('[HPM] Client disconnected');
}

View File

@@ -0,0 +1,6 @@
import { Filter, Options } from './types';
export declare type Config = {
context: Filter;
options: Options;
};
export declare function createConfig(context: any, opts?: Options): Config;

View File

@@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createConfig = void 0;
const isPlainObj = require("is-plain-obj");
const url = require("url");
const errors_1 = require("./errors");
const logger_1 = require("./logger");
const logger = (0, logger_1.getInstance)();
function createConfig(context, opts) {
// structure of config object to be returned
const config = {
context: undefined,
options: {},
};
// app.use('/api', proxy({target:'http://localhost:9000'}));
if (isContextless(context, opts)) {
config.context = '/';
config.options = Object.assign(config.options, context);
// app.use('/api', proxy('http://localhost:9000'));
// app.use(proxy('http://localhost:9000/api'));
}
else if (isStringShortHand(context)) {
const oUrl = url.parse(context);
const target = [oUrl.protocol, '//', oUrl.host].join('');
config.context = oUrl.pathname || '/';
config.options = Object.assign(config.options, { target }, opts);
if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') {
config.options.ws = true;
}
// app.use('/api', proxy({target:'http://localhost:9000'}));
}
else {
config.context = context;
config.options = Object.assign(config.options, opts);
}
configureLogger(config.options);
if (!config.options.target && !config.options.router) {
throw new Error(errors_1.ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING);
}
return config;
}
exports.createConfig = createConfig;
/**
* Checks if a String only target/config is provided.
* This can be just the host or with the optional path.
*
* @example
* app.use('/api', proxy('http://localhost:9000'));
* app.use(proxy('http://localhost:9000/api'));
*
* @param {String} context [description]
* @return {Boolean} [description]
*/
function isStringShortHand(context) {
if (typeof context === 'string') {
return !!url.parse(context).host;
}
}
/**
* Checks if a Object only config is provided, without a context.
* In this case the all paths will be proxied.
*
* @example
* app.use('/api', proxy({target:'http://localhost:9000'}));
*
* @param {Object} context [description]
* @param {*} opts [description]
* @return {Boolean} [description]
*/
function isContextless(context, opts) {
return isPlainObj(context) && (opts == null || Object.keys(opts).length === 0);
}
function configureLogger(options) {
if (options.logLevel) {
logger.setLevel(options.logLevel);
}
if (options.logProvider) {
logger.setProvider(options.logProvider);
}
}

View File

@@ -0,0 +1,2 @@
import type { Filter, Request } from './types';
export declare function match(context: Filter, uri: string, req: Request): boolean;

View File

@@ -0,0 +1,81 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.match = void 0;
const isGlob = require("is-glob");
const micromatch = require("micromatch");
const url = require("url");
const errors_1 = require("./errors");
function match(context, uri, req) {
// single path
if (isStringPath(context)) {
return matchSingleStringPath(context, uri);
}
// single glob path
if (isGlobPath(context)) {
return matchSingleGlobPath(context, uri);
}
// multi path
if (Array.isArray(context)) {
if (context.every(isStringPath)) {
return matchMultiPath(context, uri);
}
if (context.every(isGlobPath)) {
return matchMultiGlobPath(context, uri);
}
throw new Error(errors_1.ERRORS.ERR_CONTEXT_MATCHER_INVALID_ARRAY);
}
// custom matching
if (typeof context === 'function') {
const pathname = getUrlPathName(uri);
return context(pathname, req);
}
throw new Error(errors_1.ERRORS.ERR_CONTEXT_MATCHER_GENERIC);
}
exports.match = match;
/**
* @param {String} context '/api'
* @param {String} uri 'http://example.org/api/b/c/d.html'
* @return {Boolean}
*/
function matchSingleStringPath(context, uri) {
const pathname = getUrlPathName(uri);
return pathname.indexOf(context) === 0;
}
function matchSingleGlobPath(pattern, uri) {
const pathname = getUrlPathName(uri);
const matches = micromatch([pathname], pattern);
return matches && matches.length > 0;
}
function matchMultiGlobPath(patternList, uri) {
return matchSingleGlobPath(patternList, uri);
}
/**
* @param {String} contextList ['/api', '/ajax']
* @param {String} uri 'http://example.org/api/b/c/d.html'
* @return {Boolean}
*/
function matchMultiPath(contextList, uri) {
let isMultiPath = false;
for (const context of contextList) {
if (matchSingleStringPath(context, uri)) {
isMultiPath = true;
break;
}
}
return isMultiPath;
}
/**
* Parses URI and returns RFC 3986 path
*
* @param {String} uri from req.url
* @return {String} RFC 3986 path
*/
function getUrlPathName(uri) {
return uri && url.parse(uri).pathname;
}
function isStringPath(context) {
return typeof context === 'string' && !isGlob(context);
}
function isGlobPath(context) {
return isGlob(context);
}

View File

@@ -0,0 +1,6 @@
export declare enum ERRORS {
ERR_CONFIG_FACTORY_TARGET_MISSING = "[HPM] Missing \"target\" option. Example: {target: \"http://www.example.org\"}",
ERR_CONTEXT_MATCHER_GENERIC = "[HPM] Invalid context. Expecting something like: \"/api\" or [\"/api\", \"/ajax\"]",
ERR_CONTEXT_MATCHER_INVALID_ARRAY = "[HPM] Invalid context. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]",
ERR_PATH_REWRITER_CONFIG = "[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function"
}

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ERRORS = void 0;
var ERRORS;
(function (ERRORS) {
ERRORS["ERR_CONFIG_FACTORY_TARGET_MISSING"] = "[HPM] Missing \"target\" option. Example: {target: \"http://www.example.org\"}";
ERRORS["ERR_CONTEXT_MATCHER_GENERIC"] = "[HPM] Invalid context. Expecting something like: \"/api\" or [\"/api\", \"/ajax\"]";
ERRORS["ERR_CONTEXT_MATCHER_INVALID_ARRAY"] = "[HPM] Invalid context. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]";
ERRORS["ERR_PATH_REWRITER_CONFIG"] = "[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function";
})(ERRORS = exports.ERRORS || (exports.ERRORS = {}));

View File

@@ -0,0 +1,6 @@
/// <reference types="node" />
import type * as http from 'http';
/**
* Fix proxied body if bodyParser is involved.
*/
export declare function fixRequestBody(proxyReq: http.ClientRequest, req: http.IncomingMessage): void;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fixRequestBody = void 0;
const querystring = require("querystring");
/**
* Fix proxied body if bodyParser is involved.
*/
function fixRequestBody(proxyReq, req) {
// skip fixRequestBody() when req.readableLength not 0 (bodyParser failure)
if (req.readableLength !== 0) {
return;
}
const requestBody = req.body;
if (!requestBody) {
return;
}
const contentType = proxyReq.getHeader('Content-Type');
if (!contentType) {
return;
}
const writeBody = (bodyData) => {
// deepcode ignore ContentLengthInCode: bodyParser fix
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
};
if (contentType.includes('application/json')) {
writeBody(JSON.stringify(requestBody));
}
else if (contentType.includes('application/x-www-form-urlencoded')) {
writeBody(querystring.stringify(requestBody));
}
}
exports.fixRequestBody = fixRequestBody;

View File

@@ -0,0 +1 @@
export * from './public';

View File

@@ -0,0 +1,13 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./public"), exports);

View File

@@ -0,0 +1,2 @@
export { responseInterceptor } from './response-interceptor';
export { fixRequestBody } from './fix-request-body';

View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fixRequestBody = exports.responseInterceptor = void 0;
var response_interceptor_1 = require("./response-interceptor");
Object.defineProperty(exports, "responseInterceptor", { enumerable: true, get: function () { return response_interceptor_1.responseInterceptor; } });
var fix_request_body_1 = require("./fix-request-body");
Object.defineProperty(exports, "fixRequestBody", { enumerable: true, get: function () { return fix_request_body_1.fixRequestBody; } });

View File

@@ -0,0 +1,12 @@
/// <reference types="node" />
import type * as http from 'http';
declare type Interceptor = (buffer: Buffer, proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<Buffer | string>;
/**
* Intercept responses from upstream.
* Automatically decompress (deflate, gzip, brotli).
* Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
*
* NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
*/
export declare function responseInterceptor(interceptor: Interceptor): (proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
export {};

View File

@@ -0,0 +1,86 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.responseInterceptor = void 0;
const zlib = require("zlib");
/**
* Intercept responses from upstream.
* Automatically decompress (deflate, gzip, brotli).
* Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
*
* NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
*/
function responseInterceptor(interceptor) {
return async function proxyRes(proxyRes, req, res) {
const originalProxyRes = proxyRes;
let buffer = Buffer.from('', 'utf8');
// decompress proxy response
const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding']);
// concat data stream
_proxyRes.on('data', (chunk) => (buffer = Buffer.concat([buffer, chunk])));
_proxyRes.on('end', async () => {
// copy original headers
copyHeaders(proxyRes, res);
// call interceptor with intercepted response (buffer)
const interceptedBuffer = Buffer.from(await interceptor(buffer, originalProxyRes, req, res));
// set correct content-length (with double byte character support)
res.setHeader('content-length', Buffer.byteLength(interceptedBuffer, 'utf8'));
res.write(interceptedBuffer);
res.end();
});
_proxyRes.on('error', (error) => {
res.end(`Error fetching proxied request: ${error.message}`);
});
};
}
exports.responseInterceptor = responseInterceptor;
/**
* Streaming decompression of proxy response
* source: https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L116
*/
function decompress(proxyRes, contentEncoding) {
let _proxyRes = proxyRes;
let decompress;
switch (contentEncoding) {
case 'gzip':
decompress = zlib.createGunzip();
break;
case 'br':
decompress = zlib.createBrotliDecompress();
break;
case 'deflate':
decompress = zlib.createInflate();
break;
default:
break;
}
if (decompress) {
_proxyRes.pipe(decompress);
_proxyRes = decompress;
}
return _proxyRes;
}
/**
* Copy original headers
* https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L78
*/
function copyHeaders(originalResponse, response) {
response.statusCode = originalResponse.statusCode;
response.statusMessage = originalResponse.statusMessage;
if (response.setHeader) {
let keys = Object.keys(originalResponse.headers);
// ignore chunked, brotli, gzip, deflate headers
keys = keys.filter((key) => !['content-encoding', 'transfer-encoding'].includes(key));
keys.forEach((key) => {
let value = originalResponse.headers[key];
if (key === 'set-cookie') {
// remove cookie domain
value = Array.isArray(value) ? value : [value];
value = value.map((x) => x.replace(/Domain=[^;]+?/i, ''));
}
response.setHeader(key, value);
});
}
else {
response.headers = originalResponse.headers;
}
}

View File

@@ -0,0 +1,35 @@
import type { Filter, RequestHandler, Options } from './types';
export declare class HttpProxyMiddleware {
private logger;
private config;
private wsInternalSubscribed;
private serverOnCloseSubscribed;
private proxyOptions;
private proxy;
private pathRewriter;
constructor(context: Filter | Options, opts?: Options);
middleware: RequestHandler;
private catchUpgradeRequest;
private handleUpgrade;
/**
* Determine whether request should be proxied.
*
* @private
* @param {String} context [description]
* @param {Object} req [description]
* @return {Boolean}
*/
private shouldProxy;
/**
* Apply option.router and option.pathRewrite
* Order matters:
* Router uses original path for routing;
* NOT the modified path, after it has been rewritten by pathRewrite
* @param {Object} req
* @return {Object} proxy options
*/
private prepareProxyRequest;
private applyRouter;
private applyPathRewrite;
private logError;
}

View File

@@ -0,0 +1,163 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpProxyMiddleware = void 0;
const httpProxy = require("http-proxy");
const config_factory_1 = require("./config-factory");
const contextMatcher = require("./context-matcher");
const handlers = require("./_handlers");
const logger_1 = require("./logger");
const PathRewriter = require("./path-rewriter");
const Router = require("./router");
class HttpProxyMiddleware {
constructor(context, opts) {
this.logger = (0, logger_1.getInstance)();
this.wsInternalSubscribed = false;
this.serverOnCloseSubscribed = false;
// https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
this.middleware = async (req, res, next) => {
var _a, _b;
if (this.shouldProxy(this.config.context, req)) {
try {
const activeProxyOptions = await this.prepareProxyRequest(req);
this.proxy.web(req, res, activeProxyOptions);
}
catch (err) {
next(err);
}
}
else {
next();
}
/**
* Get the server object to subscribe to server events;
* 'upgrade' for websocket and 'close' for graceful shutdown
*
* NOTE:
* req.socket: node >= 13
* req.connection: node < 13 (Remove this when node 12/13 support is dropped)
*/
const server = (_b = ((_a = req.socket) !== null && _a !== void 0 ? _a : req.connection)) === null || _b === void 0 ? void 0 : _b.server;
if (server && !this.serverOnCloseSubscribed) {
server.on('close', () => {
this.logger.info('[HPM] server close signal received: closing proxy server');
this.proxy.close();
});
this.serverOnCloseSubscribed = true;
}
if (this.proxyOptions.ws === true) {
// use initial request to access the server object to subscribe to http upgrade event
this.catchUpgradeRequest(server);
}
};
this.catchUpgradeRequest = (server) => {
if (!this.wsInternalSubscribed) {
server.on('upgrade', this.handleUpgrade);
// prevent duplicate upgrade handling;
// in case external upgrade is also configured
this.wsInternalSubscribed = true;
}
};
this.handleUpgrade = async (req, socket, head) => {
if (this.shouldProxy(this.config.context, req)) {
const activeProxyOptions = await this.prepareProxyRequest(req);
this.proxy.ws(req, socket, head, activeProxyOptions);
this.logger.info('[HPM] Upgrading to WebSocket');
}
};
/**
* Determine whether request should be proxied.
*
* @private
* @param {String} context [description]
* @param {Object} req [description]
* @return {Boolean}
*/
this.shouldProxy = (context, req) => {
try {
const path = req.originalUrl || req.url;
return contextMatcher.match(context, path, req);
}
catch (error) {
this.logger.error(error);
return false;
}
};
/**
* Apply option.router and option.pathRewrite
* Order matters:
* Router uses original path for routing;
* NOT the modified path, after it has been rewritten by pathRewrite
* @param {Object} req
* @return {Object} proxy options
*/
this.prepareProxyRequest = async (req) => {
// https://github.com/chimurai/http-proxy-middleware/issues/17
// https://github.com/chimurai/http-proxy-middleware/issues/94
req.url = req.originalUrl || req.url;
// store uri before it gets rewritten for logging
const originalPath = req.url;
const newProxyOptions = Object.assign({}, this.proxyOptions);
// Apply in order:
// 1. option.router
// 2. option.pathRewrite
await this.applyRouter(req, newProxyOptions);
await this.applyPathRewrite(req, this.pathRewriter);
// debug logging for both http(s) and websockets
if (this.proxyOptions.logLevel === 'debug') {
const arrow = (0, logger_1.getArrow)(originalPath, req.url, this.proxyOptions.target, newProxyOptions.target);
this.logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target);
}
return newProxyOptions;
};
// Modify option.target when router present.
this.applyRouter = async (req, options) => {
let newTarget;
if (options.router) {
newTarget = await Router.getTarget(req, options);
if (newTarget) {
this.logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget);
options.target = newTarget;
}
}
};
// rewrite path
this.applyPathRewrite = async (req, pathRewriter) => {
if (pathRewriter) {
const path = await pathRewriter(req.url, req);
if (typeof path === 'string') {
req.url = path;
}
else {
this.logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url);
}
}
};
this.logError = (err, req, res, target) => {
var _a;
const hostname = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
const requestHref = `${hostname}${req.url}`;
const targetHref = `${target === null || target === void 0 ? void 0 : target.href}`; // target is undefined when websocket errors
const errorMessage = '[HPM] Error occurred while proxying request %s to %s [%s] (%s)';
const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
this.logger.error(errorMessage, requestHref, targetHref, err.code || err, errReference);
};
this.config = (0, config_factory_1.createConfig)(context, opts);
this.proxyOptions = this.config.options;
// create proxy
this.proxy = httpProxy.createProxyServer({});
this.logger.info(`[HPM] Proxy created: ${this.config.context} -> ${this.proxyOptions.target}`);
this.pathRewriter = PathRewriter.createPathRewriter(this.proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided
// attach handler to http-proxy events
handlers.init(this.proxy, this.proxyOptions);
// log errors for debug purpose
this.proxy.on('error', this.logError);
// https://github.com/chimurai/http-proxy-middleware/issues/19
// expose function to upgrade externally
this.middleware.upgrade = (req, socket, head) => {
if (!this.wsInternalSubscribed) {
this.handleUpgrade(req, socket, head);
}
};
}
}
exports.HttpProxyMiddleware = HttpProxyMiddleware;

View File

@@ -0,0 +1,4 @@
import { Filter, Options } from './types';
export declare function createProxyMiddleware(context: Filter | Options, options?: Options): import("./types").RequestHandler;
export * from './handlers';
export { Filter, Options, RequestHandler } from './types';

View File

@@ -0,0 +1,20 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createProxyMiddleware = void 0;
const http_proxy_middleware_1 = require("./http-proxy-middleware");
function createProxyMiddleware(context, options) {
const { middleware } = new http_proxy_middleware_1.HttpProxyMiddleware(context, options);
return middleware;
}
exports.createProxyMiddleware = createProxyMiddleware;
__exportStar(require("./handlers"), exports);

View File

@@ -0,0 +1,14 @@
export declare function getInstance(): any;
/**
* -> normal proxy
* => router
* ~> pathRewrite
* ≈> router + pathRewrite
*
* @param {String} originalPath
* @param {String} newPath
* @param {String} originalTarget
* @param {String} newTarget
* @return {String}
*/
export declare function getArrow(originalPath: any, newPath: any, originalTarget: any, newTarget: any): string;

View File

@@ -0,0 +1,135 @@
"use strict";
/* eslint-disable prefer-rest-params */
Object.defineProperty(exports, "__esModule", { value: true });
exports.getArrow = exports.getInstance = void 0;
const util = require("util");
let loggerInstance;
const defaultProvider = {
// tslint:disable: no-console
log: console.log,
debug: console.log,
info: console.info,
warn: console.warn,
error: console.error,
};
// log level 'weight'
var LEVELS;
(function (LEVELS) {
LEVELS[LEVELS["debug"] = 10] = "debug";
LEVELS[LEVELS["info"] = 20] = "info";
LEVELS[LEVELS["warn"] = 30] = "warn";
LEVELS[LEVELS["error"] = 50] = "error";
LEVELS[LEVELS["silent"] = 80] = "silent";
})(LEVELS || (LEVELS = {}));
function getInstance() {
if (!loggerInstance) {
loggerInstance = new Logger();
}
return loggerInstance;
}
exports.getInstance = getInstance;
class Logger {
constructor() {
this.setLevel('info');
this.setProvider(() => defaultProvider);
}
// log will log messages, regardless of logLevels
log() {
this.provider.log(this._interpolate.apply(null, arguments));
}
debug() {
if (this._showLevel('debug')) {
this.provider.debug(this._interpolate.apply(null, arguments));
}
}
info() {
if (this._showLevel('info')) {
this.provider.info(this._interpolate.apply(null, arguments));
}
}
warn() {
if (this._showLevel('warn')) {
this.provider.warn(this._interpolate.apply(null, arguments));
}
}
error() {
if (this._showLevel('error')) {
this.provider.error(this._interpolate.apply(null, arguments));
}
}
setLevel(v) {
if (this.isValidLevel(v)) {
this.logLevel = v;
}
}
setProvider(fn) {
if (fn && this.isValidProvider(fn)) {
this.provider = fn(defaultProvider);
}
}
isValidProvider(fnProvider) {
const result = true;
if (fnProvider && typeof fnProvider !== 'function') {
throw new Error('[HPM] Log provider config error. Expecting a function.');
}
return result;
}
isValidLevel(levelName) {
const validLevels = Object.keys(LEVELS);
const isValid = validLevels.includes(levelName);
if (!isValid) {
throw new Error('[HPM] Log level error. Invalid logLevel.');
}
return isValid;
}
/**
* Decide to log or not to log, based on the log levels 'weight'
* @param {String} showLevel [debug, info, warn, error, silent]
* @return {Boolean}
*/
_showLevel(showLevel) {
let result = false;
const currentLogLevel = LEVELS[this.logLevel];
if (currentLogLevel && currentLogLevel <= LEVELS[showLevel]) {
result = true;
}
return result;
}
// make sure logged messages and its data are return interpolated
// make it possible for additional log data, such date/time or custom prefix.
_interpolate(format, ...args) {
const result = util.format(format, ...args);
return result;
}
}
/**
* -> normal proxy
* => router
* ~> pathRewrite
* ≈> router + pathRewrite
*
* @param {String} originalPath
* @param {String} newPath
* @param {String} originalTarget
* @param {String} newTarget
* @return {String}
*/
function getArrow(originalPath, newPath, originalTarget, newTarget) {
const arrow = ['>'];
const isNewTarget = originalTarget !== newTarget; // router
const isNewPath = originalPath !== newPath; // pathRewrite
if (isNewPath && !isNewTarget) {
arrow.unshift('~');
}
else if (!isNewPath && isNewTarget) {
arrow.unshift('=');
}
else if (isNewPath && isNewTarget) {
arrow.unshift('≈');
}
else {
arrow.unshift('-');
}
return arrow.join('');
}
exports.getArrow = getArrow;

View File

@@ -0,0 +1,7 @@
/**
* Create rewrite function, to cache parsed rewrite rules.
*
* @param {Object} rewriteConfig
* @return {Function} Function to rewrite paths; This function should accept `path` (request.url) as parameter
*/
export declare function createPathRewriter(rewriteConfig: any): any;

View File

@@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPathRewriter = void 0;
const isPlainObj = require("is-plain-obj");
const errors_1 = require("./errors");
const logger_1 = require("./logger");
const logger = (0, logger_1.getInstance)();
/**
* Create rewrite function, to cache parsed rewrite rules.
*
* @param {Object} rewriteConfig
* @return {Function} Function to rewrite paths; This function should accept `path` (request.url) as parameter
*/
function createPathRewriter(rewriteConfig) {
let rulesCache;
if (!isValidRewriteConfig(rewriteConfig)) {
return;
}
if (typeof rewriteConfig === 'function') {
const customRewriteFn = rewriteConfig;
return customRewriteFn;
}
else {
rulesCache = parsePathRewriteRules(rewriteConfig);
return rewritePath;
}
function rewritePath(path) {
let result = path;
for (const rule of rulesCache) {
if (rule.regex.test(path)) {
result = result.replace(rule.regex, rule.value);
logger.debug('[HPM] Rewriting path from "%s" to "%s"', path, result);
break;
}
}
return result;
}
}
exports.createPathRewriter = createPathRewriter;
function isValidRewriteConfig(rewriteConfig) {
if (typeof rewriteConfig === 'function') {
return true;
}
else if (isPlainObj(rewriteConfig)) {
return Object.keys(rewriteConfig).length !== 0;
}
else if (rewriteConfig === undefined || rewriteConfig === null) {
return false;
}
else {
throw new Error(errors_1.ERRORS.ERR_PATH_REWRITER_CONFIG);
}
}
function parsePathRewriteRules(rewriteConfig) {
const rules = [];
if (isPlainObj(rewriteConfig)) {
for (const [key] of Object.entries(rewriteConfig)) {
rules.push({
regex: new RegExp(key),
value: rewriteConfig[key],
});
logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, rewriteConfig[key]);
}
}
return rules;
}

View File

@@ -0,0 +1 @@
export declare function getTarget(req: any, config: any): Promise<any>;

View File

@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTarget = void 0;
const isPlainObj = require("is-plain-obj");
const logger_1 = require("./logger");
const logger = (0, logger_1.getInstance)();
async function getTarget(req, config) {
let newTarget;
const router = config.router;
if (isPlainObj(router)) {
newTarget = getTargetFromProxyTable(req, router);
}
else if (typeof router === 'function') {
newTarget = await router(req);
}
return newTarget;
}
exports.getTarget = getTarget;
function getTargetFromProxyTable(req, table) {
let result;
const host = req.headers.host;
const path = req.url;
const hostAndPath = host + path;
for (const [key] of Object.entries(table)) {
if (containsPath(key)) {
if (hostAndPath.indexOf(key) > -1) {
// match 'localhost:3000/api'
result = table[key];
logger.debug('[HPM] Router table match: "%s"', key);
break;
}
}
else {
if (key === host) {
// match 'localhost:3000'
result = table[key];
logger.debug('[HPM] Router table match: "%s"', host);
break;
}
}
}
return result;
}
function containsPath(v) {
return v.indexOf('/') > -1;
}

View File

@@ -0,0 +1,54 @@
/**
* Based on definition by DefinitelyTyped:
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/6f529c6c67a447190f86bfbf894d1061e41e07b7/types/http-proxy-middleware/index.d.ts
*/
/// <reference types="node" />
import type * as express from 'express';
import type * as http from 'http';
import type * as httpProxy from 'http-proxy';
import type * as net from 'net';
import type * as url from 'url';
export interface Request extends express.Request {
}
export interface Response extends express.Response {
}
export interface RequestHandler extends express.RequestHandler {
upgrade?: (req: Request, socket: net.Socket, head: any) => void;
}
export declare type Filter = string | string[] | ((pathname: string, req: Request) => boolean);
export interface Options extends httpProxy.ServerOptions {
pathRewrite?: {
[regexp: string]: string;
} | ((path: string, req: Request) => string) | ((path: string, req: Request) => Promise<string>);
router?: {
[hostOrPath: string]: httpProxy.ServerOptions['target'];
} | ((req: Request) => httpProxy.ServerOptions['target']) | ((req: Request) => Promise<httpProxy.ServerOptions['target']>);
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent';
logProvider?: LogProviderCallback;
onError?: OnErrorCallback;
onProxyRes?: OnProxyResCallback;
onProxyReq?: OnProxyReqCallback;
onProxyReqWs?: OnProxyReqWsCallback;
onOpen?: OnOpenCallback;
onClose?: OnCloseCallback;
}
interface LogProvider {
log: Logger;
debug?: Logger;
info?: Logger;
warn?: Logger;
error?: Logger;
}
declare type Logger = (...args: any[]) => void;
export declare type LogProviderCallback = (provider: LogProvider) => LogProvider;
/**
* Use types based on the events listeners from http-proxy
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/51504fd999031b7f025220fab279f1b2155cbaff/types/http-proxy/index.d.ts
*/
export declare type OnErrorCallback = (err: Error, req: Request, res: Response, target?: string | Partial<url.Url>) => void;
export declare type OnProxyResCallback = (proxyRes: http.IncomingMessage, req: Request, res: Response) => void;
export declare type OnProxyReqCallback = (proxyReq: http.ClientRequest, req: Request, res: Response, options: httpProxy.ServerOptions) => void;
export declare type OnProxyReqWsCallback = (proxyReq: http.ClientRequest, req: Request, socket: net.Socket, options: httpProxy.ServerOptions, head: any) => void;
export declare type OnCloseCallback = (proxyRes: Response, proxySocket: net.Socket, proxyHead: any) => void;
export declare type OnOpenCallback = (proxySocket: net.Socket) => void;
export {};

View File

@@ -0,0 +1,6 @@
"use strict";
/**
* Based on definition by DefinitelyTyped:
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/6f529c6c67a447190f86bfbf894d1061e41e07b7/types/http-proxy-middleware/index.d.ts
*/
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,109 @@
{
"name": "http-proxy-middleware",
"version": "2.0.9",
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"clean": "rm -rf dist && rm -rf coverage",
"lint": "yarn prettier && yarn eslint",
"lint:fix": "yarn prettier:fix && yarn eslint:fix",
"eslint": "eslint '{src,test}/**/*.ts'",
"eslint:fix": "yarn eslint --fix",
"prettier": "prettier --list-different \"**/*.{js,ts,md,yml,json,html}\"",
"prettier:fix": "prettier --write \"**/*.{js,ts,md,yml,json,html}\"",
"prebuild": "yarn clean",
"build": "tsc",
"pretest": "yarn build",
"test": "jest",
"precoverage": "yarn build",
"coverage": "jest --coverage --coverageReporters=lcov",
"prepare": "husky install",
"prepack": "yarn build && rm dist/tsconfig.tsbuildinfo",
"spellcheck": "npx --yes cspell --show-context --show-suggestions '**/*.*'"
},
"repository": {
"type": "git",
"url": "https://github.com/chimurai/http-proxy-middleware.git"
},
"keywords": [
"reverse",
"proxy",
"middleware",
"http",
"https",
"connect",
"express",
"fastify",
"polka",
"browser-sync",
"gulp",
"grunt-contrib-connect",
"websocket",
"ws",
"cors"
],
"author": "Steven Chim",
"license": "MIT",
"bugs": {
"url": "https://github.com/chimurai/http-proxy-middleware/issues"
},
"homepage": "https://github.com/chimurai/http-proxy-middleware#readme",
"devDependencies": {
"@commitlint/cli": "16.2.1",
"@commitlint/config-conventional": "16.2.1",
"@types/express": "4.17.13",
"@types/is-glob": "4.0.2",
"@types/jest": "27.4.0",
"@types/micromatch": "4.0.2",
"@types/node": "17.0.18",
"@types/supertest": "2.0.11",
"@types/ws": "8.2.2",
"@typescript-eslint/eslint-plugin": "5.12.0",
"@typescript-eslint/parser": "5.12.0",
"body-parser": "1.19.2",
"browser-sync": "2.27.7",
"connect": "3.7.0",
"eslint": "8.9.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-prettier": "4.0.0",
"express": "4.17.3",
"get-port": "5.1.1",
"husky": "7.0.4",
"jest": "27.5.1",
"lint-staged": "12.3.4",
"mockttp": "2.6.0",
"open": "8.4.0",
"prettier": "2.5.1",
"supertest": "6.2.2",
"ts-jest": "27.1.3",
"typescript": "4.5.5",
"ws": "8.5.0"
},
"dependencies": {
"@types/http-proxy": "^1.17.8",
"http-proxy": "^1.18.1",
"is-glob": "^4.0.1",
"is-plain-obj": "^3.0.0",
"micromatch": "^4.0.2"
},
"peerDependencies": {
"@types/express": "^4.17.13"
},
"peerDependenciesMeta": {
"@types/express": {
"optional": true
}
},
"engines": {
"node": ">=12.0.0"
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
}
}

View File

@@ -0,0 +1,136 @@
# Release history
**All notable changes to this project will be documented in this file.**
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
<details>
<summary><strong>Guiding Principles</strong></summary>
- Changelogs are for humans, not machines.
- There should be an entry for every single version.
- The same types of changes should be grouped.
- Versions and sections should be linkable.
- The latest version comes first.
- The release date of each versions is displayed.
- Mention whether you follow Semantic Versioning.
</details>
<details>
<summary><strong>Types of changes</strong></summary>
Changelog entries are classified using the following labels _(from [keep-a-changelog](http://keepachangelog.com/)_):
- `Added` for new features.
- `Changed` for changes in existing functionality.
- `Deprecated` for soon-to-be removed features.
- `Removed` for now removed features.
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.
</details>
## 2.3.1 (2022-01-02)
### Fixed
* Fixes bug when a pattern containing an expression after the closing parenthesis (`/!(*.d).{ts,tsx}`) was incorrectly converted to regexp ([9f241ef](https://github.com/micromatch/picomatch/commit/9f241ef)).
### Changed
* Some documentation improvements ([f81d236](https://github.com/micromatch/picomatch/commit/f81d236), [421e0e7](https://github.com/micromatch/picomatch/commit/421e0e7)).
## 2.3.0 (2021-05-21)
### Fixed
* Fixes bug where file names with two dots were not being matched consistently with negation extglobs containing a star ([56083ef](https://github.com/micromatch/picomatch/commit/56083ef))
## 2.2.3 (2021-04-10)
### Fixed
* Do not skip pattern seperator for square brackets ([fb08a30](https://github.com/micromatch/picomatch/commit/fb08a30)).
* Set negatedExtGlob also if it does not span the whole pattern ([032e3f5](https://github.com/micromatch/picomatch/commit/032e3f5)).
## 2.2.2 (2020-03-21)
### Fixed
* Correctly handle parts of the pattern after parentheses in the `scan` method ([e15b920](https://github.com/micromatch/picomatch/commit/e15b920)).
## 2.2.1 (2020-01-04)
* Fixes [#49](https://github.com/micromatch/picomatch/issues/49), so that braces with no sets or ranges are now propertly treated as literals.
## 2.2.0 (2020-01-04)
* Disable fastpaths mode for the parse method ([5b8d33f](https://github.com/micromatch/picomatch/commit/5b8d33f))
* Add `tokens`, `slashes`, and `parts` to the object returned by `picomatch.scan()`.
## 2.1.0 (2019-10-31)
* add benchmarks for scan ([4793b92](https://github.com/micromatch/picomatch/commit/4793b92))
* Add eslint object-curly-spacing rule ([707c650](https://github.com/micromatch/picomatch/commit/707c650))
* Add prefer-const eslint rule ([5c7501c](https://github.com/micromatch/picomatch/commit/5c7501c))
* Add support for nonegate in scan API ([275c9b9](https://github.com/micromatch/picomatch/commit/275c9b9))
* Change lets to consts. Move root import up. ([4840625](https://github.com/micromatch/picomatch/commit/4840625))
* closes https://github.com/micromatch/picomatch/issues/21 ([766bcb0](https://github.com/micromatch/picomatch/commit/766bcb0))
* Fix "Extglobs" table in readme ([eb19da8](https://github.com/micromatch/picomatch/commit/eb19da8))
* fixes https://github.com/micromatch/picomatch/issues/20 ([9caca07](https://github.com/micromatch/picomatch/commit/9caca07))
* fixes https://github.com/micromatch/picomatch/issues/26 ([fa58f45](https://github.com/micromatch/picomatch/commit/fa58f45))
* Lint test ([d433a34](https://github.com/micromatch/picomatch/commit/d433a34))
* lint unit tests ([0159b55](https://github.com/micromatch/picomatch/commit/0159b55))
* Make scan work with noext ([6c02e03](https://github.com/micromatch/picomatch/commit/6c02e03))
* minor linting ([c2a2b87](https://github.com/micromatch/picomatch/commit/c2a2b87))
* minor parser improvements ([197671d](https://github.com/micromatch/picomatch/commit/197671d))
* remove eslint since it... ([07876fa](https://github.com/micromatch/picomatch/commit/07876fa))
* remove funding file ([8ebe96d](https://github.com/micromatch/picomatch/commit/8ebe96d))
* Remove unused funks ([cbc6d54](https://github.com/micromatch/picomatch/commit/cbc6d54))
* Run eslint during pretest, fix existing eslint findings ([0682367](https://github.com/micromatch/picomatch/commit/0682367))
* support `noparen` in scan ([3d37569](https://github.com/micromatch/picomatch/commit/3d37569))
* update changelog ([7b34e77](https://github.com/micromatch/picomatch/commit/7b34e77))
* update travis ([777f038](https://github.com/micromatch/picomatch/commit/777f038))
* Use eslint-disable-next-line instead of eslint-disable ([4e7c1fd](https://github.com/micromatch/picomatch/commit/4e7c1fd))
## 2.0.7 (2019-05-14)
* 2.0.7 ([9eb9a71](https://github.com/micromatch/picomatch/commit/9eb9a71))
* supports lookbehinds ([1f63f7e](https://github.com/micromatch/picomatch/commit/1f63f7e))
* update .verb.md file with typo change ([2741279](https://github.com/micromatch/picomatch/commit/2741279))
* fix: typo in README ([0753e44](https://github.com/micromatch/picomatch/commit/0753e44))
## 2.0.4 (2019-04-10)
### Fixed
- Readme link [fixed](https://github.com/micromatch/picomatch/pull/13/commits/a96ab3aa2b11b6861c23289964613d85563b05df) by @danez.
- `options.capture` now works as expected when fastpaths are enabled. See https://github.com/micromatch/picomatch/pull/12/commits/26aefd71f1cfaf95c37f1c1fcab68a693b037304. Thanks to @DrPizza.
## 2.0.0 (2019-04-10)
### Added
- Adds support for `options.onIgnore`. See the readme for details
- Adds support for `options.onResult`. See the readme for details
### Breaking changes
- The unixify option was renamed to `windows`
- caching and all related options and methods have been removed
## 1.0.0 (2018-11-05)
- adds `.onMatch` option
- improvements to `.scan` method
- numerous improvements and optimizations for matching and parsing
- better windows path handling
## 0.1.0 - 2017-04-13
First release.
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog

View File

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

View File

@@ -0,0 +1,708 @@
<h1 align="center">Picomatch</h1>
<p align="center">
<a href="https://npmjs.org/package/picomatch">
<img src="https://img.shields.io/npm/v/picomatch.svg" alt="version">
</a>
<a href="https://github.com/micromatch/picomatch/actions?workflow=Tests">
<img src="https://github.com/micromatch/picomatch/workflows/Tests/badge.svg" alt="test status">
</a>
<a href="https://coveralls.io/github/micromatch/picomatch">
<img src="https://img.shields.io/coveralls/github/micromatch/picomatch/master.svg" alt="coverage status">
</a>
<a href="https://npmjs.org/package/picomatch">
<img src="https://img.shields.io/npm/dm/picomatch.svg" alt="downloads">
</a>
</p>
<br>
<br>
<p align="center">
<strong>Blazing fast and accurate glob matcher written in JavaScript.</strong></br>
<em>No dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions.</em>
</p>
<br>
<br>
## Why picomatch?
* **Lightweight** - No dependencies
* **Minimal** - Tiny API surface. Main export is a function that takes a glob pattern and returns a matcher function.
* **Fast** - Loads in about 2ms (that's several times faster than a [single frame of a HD movie](http://www.endmemo.com/sconvert/framespersecondframespermillisecond.php) at 60fps)
* **Performant** - Use the returned matcher function to speed up repeat matching (like when watching files)
* **Accurate matching** - Using wildcards (`*` and `?`), globstars (`**`) for nested directories, [advanced globbing](#advanced-globbing) with extglobs, braces, and POSIX brackets, and support for escaping special characters with `\` or quotes.
* **Well tested** - Thousands of unit tests
See the [library comparison](#library-comparisons) to other libraries.
<br>
<br>
## Table of Contents
<details><summary> Click to expand </summary>
- [Install](#install)
- [Usage](#usage)
- [API](#api)
* [picomatch](#picomatch)
* [.test](#test)
* [.matchBase](#matchbase)
* [.isMatch](#ismatch)
* [.parse](#parse)
* [.scan](#scan)
* [.compileRe](#compilere)
* [.makeRe](#makere)
* [.toRegex](#toregex)
- [Options](#options)
* [Picomatch options](#picomatch-options)
* [Scan Options](#scan-options)
* [Options Examples](#options-examples)
- [Globbing features](#globbing-features)
* [Basic globbing](#basic-globbing)
* [Advanced globbing](#advanced-globbing)
* [Braces](#braces)
* [Matching special characters as literals](#matching-special-characters-as-literals)
- [Library Comparisons](#library-comparisons)
- [Benchmarks](#benchmarks)
- [Philosophies](#philosophies)
- [About](#about)
* [Author](#author)
* [License](#license)
_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_
</details>
<br>
<br>
## Install
Install with [npm](https://www.npmjs.com/):
```sh
npm install --save picomatch
```
<br>
## Usage
The main export is a function that takes a glob pattern and an options object and returns a function for matching strings.
```js
const pm = require('picomatch');
const isMatch = pm('*.js');
console.log(isMatch('abcd')); //=> false
console.log(isMatch('a.js')); //=> true
console.log(isMatch('a.md')); //=> false
console.log(isMatch('a/b.js')); //=> false
```
<br>
## API
### [picomatch](lib/picomatch.js#L32)
Creates a matcher function from one or more glob patterns. The returned function takes a string to match as its first argument, and returns true if the string is a match. The returned matcher function also takes a boolean as the second argument that, when true, returns an object with additional information.
**Params**
* `globs` **{String|Array}**: One or more glob patterns.
* `options` **{Object=}**
* `returns` **{Function=}**: Returns a matcher function.
**Example**
```js
const picomatch = require('picomatch');
// picomatch(glob[, options]);
const isMatch = picomatch('*.!(*a)');
console.log(isMatch('a.a')); //=> false
console.log(isMatch('a.b')); //=> true
```
### [.test](lib/picomatch.js#L117)
Test `input` with the given `regex`. This is used by the main `picomatch()` function to test the input string.
**Params**
* `input` **{String}**: String to test.
* `regex` **{RegExp}**
* `returns` **{Object}**: Returns an object with matching info.
**Example**
```js
const picomatch = require('picomatch');
// picomatch.test(input, regex[, options]);
console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
// { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }
```
### [.matchBase](lib/picomatch.js#L161)
Match the basename of a filepath.
**Params**
* `input` **{String}**: String to test.
* `glob` **{RegExp|String}**: Glob pattern or regex created by [.makeRe](#makeRe).
* `returns` **{Boolean}**
**Example**
```js
const picomatch = require('picomatch');
// picomatch.matchBase(input, glob[, options]);
console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
```
### [.isMatch](lib/picomatch.js#L183)
Returns true if **any** of the given glob `patterns` match the specified `string`.
**Params**
* **{String|Array}**: str The string to test.
* **{String|Array}**: patterns One or more glob patterns to use for matching.
* **{Object}**: See available [options](#options).
* `returns` **{Boolean}**: Returns true if any patterns match `str`
**Example**
```js
const picomatch = require('picomatch');
// picomatch.isMatch(string, patterns[, options]);
console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
console.log(picomatch.isMatch('a.a', 'b.*')); //=> false
```
### [.parse](lib/picomatch.js#L199)
Parse a glob pattern to create the source string for a regular expression.
**Params**
* `pattern` **{String}**
* `options` **{Object}**
* `returns` **{Object}**: Returns an object with useful properties and output to be used as a regex source string.
**Example**
```js
const picomatch = require('picomatch');
const result = picomatch.parse(pattern[, options]);
```
### [.scan](lib/picomatch.js#L231)
Scan a glob pattern to separate the pattern into segments.
**Params**
* `input` **{String}**: Glob pattern to scan.
* `options` **{Object}**
* `returns` **{Object}**: Returns an object with
**Example**
```js
const picomatch = require('picomatch');
// picomatch.scan(input[, options]);
const result = picomatch.scan('!./foo/*.js');
console.log(result);
{ prefix: '!./',
input: '!./foo/*.js',
start: 3,
base: 'foo',
glob: '*.js',
isBrace: false,
isBracket: false,
isGlob: true,
isExtglob: false,
isGlobstar: false,
negated: true }
```
### [.compileRe](lib/picomatch.js#L245)
Compile a regular expression from the `state` object returned by the
[parse()](#parse) method.
**Params**
* `state` **{Object}**
* `options` **{Object}**
* `returnOutput` **{Boolean}**: Intended for implementors, this argument allows you to return the raw output from the parser.
* `returnState` **{Boolean}**: Adds the state to a `state` property on the returned regex. Useful for implementors and debugging.
* `returns` **{RegExp}**
### [.makeRe](lib/picomatch.js#L286)
Create a regular expression from a parsed glob pattern.
**Params**
* `state` **{String}**: The object returned from the `.parse` method.
* `options` **{Object}**
* `returnOutput` **{Boolean}**: Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result.
* `returnState` **{Boolean}**: Implementors may use this argument to return the state from the parsed glob with the returned regular expression.
* `returns` **{RegExp}**: Returns a regex created from the given pattern.
**Example**
```js
const picomatch = require('picomatch');
const state = picomatch.parse('*.js');
// picomatch.compileRe(state[, options]);
console.log(picomatch.compileRe(state));
//=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
```
### [.toRegex](lib/picomatch.js#L321)
Create a regular expression from the given regex source string.
**Params**
* `source` **{String}**: Regular expression source string.
* `options` **{Object}**
* `returns` **{RegExp}**
**Example**
```js
const picomatch = require('picomatch');
// picomatch.toRegex(source[, options]);
const { output } = picomatch.parse('*.js');
console.log(picomatch.toRegex(output));
//=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
```
<br>
## Options
### Picomatch options
The following options may be used with the main `picomatch()` function or any of the methods on the picomatch API.
| **Option** | **Type** | **Default value** | **Description** |
| --- | --- | --- | --- |
| `basename` | `boolean` | `false` | If set, then patterns without slashes will be matched against the basename of the path if it contains slashes. For example, `a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`. |
| `bash` | `boolean` | `false` | Follow bash matching rules more strictly - disallows backslashes as escape characters, and treats single stars as globstars (`**`). |
| `capture` | `boolean` | `undefined` | Return regex matches in supporting methods. |
| `contains` | `boolean` | `undefined` | Allows glob to match any part of the given string(s). |
| `cwd` | `string` | `process.cwd()` | Current working directory. Used by `picomatch.split()` |
| `debug` | `boolean` | `undefined` | Debug regular expressions when an error is thrown. |
| `dot` | `boolean` | `false` | Enable dotfile matching. By default, dotfiles are ignored unless a `.` is explicitly defined in the pattern, or `options.dot` is true |
| `expandRange` | `function` | `undefined` | Custom function for expanding ranges in brace patterns, such as `{a..z}`. The function receives the range values as two arguments, and it must return a string to be used in the generated regex. It's recommended that returned strings be wrapped in parentheses. |
| `failglob` | `boolean` | `false` | Throws an error if no matches are found. Based on the bash option of the same name. |
| `fastpaths` | `boolean` | `true` | To speed up processing, full parsing is skipped for a handful common glob patterns. Disable this behavior by setting this option to `false`. |
| `flags` | `string` | `undefined` | Regex flags to use in the generated regex. If defined, the `nocase` option will be overridden. |
| [format](#optionsformat) | `function` | `undefined` | Custom function for formatting the returned string. This is useful for removing leading slashes, converting Windows paths to Posix paths, etc. |
| `ignore` | `array\|string` | `undefined` | One or more glob patterns for excluding strings that should not be matched from the result. |
| `keepQuotes` | `boolean` | `false` | Retain quotes in the generated regex, since quotes may also be used as an alternative to backslashes. |
| `literalBrackets` | `boolean` | `undefined` | When `true`, brackets in the glob pattern will be escaped so that only literal brackets will be matched. |
| `matchBase` | `boolean` | `false` | Alias for `basename` |
| `maxLength` | `boolean` | `65536` | Limit the max length of the input string. An error is thrown if the input string is longer than this value. |
| `nobrace` | `boolean` | `false` | Disable brace matching, so that `{a,b}` and `{1..3}` would be treated as literal characters. |
| `nobracket` | `boolean` | `undefined` | Disable matching with regex brackets. |
| `nocase` | `boolean` | `false` | Make matching case-insensitive. Equivalent to the regex `i` flag. Note that this option is overridden by the `flags` option. |
| `nodupes` | `boolean` | `true` | Deprecated, use `nounique` instead. This option will be removed in a future major release. By default duplicates are removed. Disable uniquification by setting this option to false. |
| `noext` | `boolean` | `false` | Alias for `noextglob` |
| `noextglob` | `boolean` | `false` | Disable support for matching with extglobs (like `+(a\|b)`) |
| `noglobstar` | `boolean` | `false` | Disable support for matching nested directories with globstars (`**`) |
| `nonegate` | `boolean` | `false` | Disable support for negating with leading `!` |
| `noquantifiers` | `boolean` | `false` | Disable support for regex quantifiers (like `a{1,2}`) and treat them as brace patterns to be expanded. |
| [onIgnore](#optionsonIgnore) | `function` | `undefined` | Function to be called on ignored items. |
| [onMatch](#optionsonMatch) | `function` | `undefined` | Function to be called on matched items. |
| [onResult](#optionsonResult) | `function` | `undefined` | Function to be called on all items, regardless of whether or not they are matched or ignored. |
| `posix` | `boolean` | `false` | Support POSIX character classes ("posix brackets"). |
| `posixSlashes` | `boolean` | `undefined` | Convert all slashes in file paths to forward slashes. This does not convert slashes in the glob pattern itself |
| `prepend` | `boolean` | `undefined` | String to prepend to the generated regex used for matching. |
| `regex` | `boolean` | `false` | Use regular expression rules for `+` (instead of matching literal `+`), and for stars that follow closing parentheses or brackets (as in `)*` and `]*`). |
| `strictBrackets` | `boolean` | `undefined` | Throw an error if brackets, braces, or parens are imbalanced. |
| `strictSlashes` | `boolean` | `undefined` | When true, picomatch won't match trailing slashes with single stars. |
| `unescape` | `boolean` | `undefined` | Remove backslashes preceding escaped characters in the glob pattern. By default, backslashes are retained. |
| `unixify` | `boolean` | `undefined` | Alias for `posixSlashes`, for backwards compatibility. |
picomatch has automatic detection for regex positive and negative lookbehinds. If the pattern contains a negative lookbehind, you must be using Node.js >= 8.10 or else picomatch will throw an error.
### Scan Options
In addition to the main [picomatch options](#picomatch-options), the following options may also be used with the [.scan](#scan) method.
| **Option** | **Type** | **Default value** | **Description** |
| --- | --- | --- | --- |
| `tokens` | `boolean` | `false` | When `true`, the returned object will include an array of tokens (objects), representing each path "segment" in the scanned glob pattern |
| `parts` | `boolean` | `false` | When `true`, the returned object will include an array of strings representing each path "segment" in the scanned glob pattern. This is automatically enabled when `options.tokens` is true |
**Example**
```js
const picomatch = require('picomatch');
const result = picomatch.scan('!./foo/*.js', { tokens: true });
console.log(result);
// {
// prefix: '!./',
// input: '!./foo/*.js',
// start: 3,
// base: 'foo',
// glob: '*.js',
// isBrace: false,
// isBracket: false,
// isGlob: true,
// isExtglob: false,
// isGlobstar: false,
// negated: true,
// maxDepth: 2,
// tokens: [
// { value: '!./', depth: 0, isGlob: false, negated: true, isPrefix: true },
// { value: 'foo', depth: 1, isGlob: false },
// { value: '*.js', depth: 1, isGlob: true }
// ],
// slashes: [ 2, 6 ],
// parts: [ 'foo', '*.js' ]
// }
```
<br>
### Options Examples
#### options.expandRange
**Type**: `function`
**Default**: `undefined`
Custom function for expanding ranges in brace patterns. The [fill-range](https://github.com/jonschlinkert/fill-range) library is ideal for this purpose, or you can use custom code to do whatever you need.
**Example**
The following example shows how to create a glob that matches a folder
```js
const fill = require('fill-range');
const regex = pm.makeRe('foo/{01..25}/bar', {
expandRange(a, b) {
return `(${fill(a, b, { toRegex: true })})`;
}
});
console.log(regex);
//=> /^(?:foo\/((?:0[1-9]|1[0-9]|2[0-5]))\/bar)$/
console.log(regex.test('foo/00/bar')) // false
console.log(regex.test('foo/01/bar')) // true
console.log(regex.test('foo/10/bar')) // true
console.log(regex.test('foo/22/bar')) // true
console.log(regex.test('foo/25/bar')) // true
console.log(regex.test('foo/26/bar')) // false
```
#### options.format
**Type**: `function`
**Default**: `undefined`
Custom function for formatting strings before they're matched.
**Example**
```js
// strip leading './' from strings
const format = str => str.replace(/^\.\//, '');
const isMatch = picomatch('foo/*.js', { format });
console.log(isMatch('./foo/bar.js')); //=> true
```
#### options.onMatch
```js
const onMatch = ({ glob, regex, input, output }) => {
console.log({ glob, regex, input, output });
};
const isMatch = picomatch('*', { onMatch });
isMatch('foo');
isMatch('bar');
isMatch('baz');
```
#### options.onIgnore
```js
const onIgnore = ({ glob, regex, input, output }) => {
console.log({ glob, regex, input, output });
};
const isMatch = picomatch('*', { onIgnore, ignore: 'f*' });
isMatch('foo');
isMatch('bar');
isMatch('baz');
```
#### options.onResult
```js
const onResult = ({ glob, regex, input, output }) => {
console.log({ glob, regex, input, output });
};
const isMatch = picomatch('*', { onResult, ignore: 'f*' });
isMatch('foo');
isMatch('bar');
isMatch('baz');
```
<br>
<br>
## Globbing features
* [Basic globbing](#basic-globbing) (Wildcard matching)
* [Advanced globbing](#advanced-globbing) (extglobs, posix brackets, brace matching)
### Basic globbing
| **Character** | **Description** |
| --- | --- |
| `*` | Matches any character zero or more times, excluding path separators. Does _not match_ path separators or hidden files or directories ("dotfiles"), unless explicitly enabled by setting the `dot` option to `true`. |
| `**` | Matches any character zero or more times, including path separators. Note that `**` will only match path separators (`/`, and `\\` on Windows) when they are the only characters in a path segment. Thus, `foo**/bar` is equivalent to `foo*/bar`, and `foo/a**b/bar` is equivalent to `foo/a*b/bar`, and _more than two_ consecutive stars in a glob path segment are regarded as _a single star_. Thus, `foo/***/bar` is equivalent to `foo/*/bar`. |
| `?` | Matches any character excluding path separators one time. Does _not match_ path separators or leading dots. |
| `[abc]` | Matches any characters inside the brackets. For example, `[abc]` would match the characters `a`, `b` or `c`, and nothing else. |
#### Matching behavior vs. Bash
Picomatch's matching features and expected results in unit tests are based on Bash's unit tests and the Bash 4.3 specification, with the following exceptions:
* Bash will match `foo/bar/baz` with `*`. Picomatch only matches nested directories with `**`.
* Bash greedily matches with negated extglobs. For example, Bash 4.3 says that `!(foo)*` should match `foo` and `foobar`, since the trailing `*` bracktracks to match the preceding pattern. This is very memory-inefficient, and IMHO, also incorrect. Picomatch would return `false` for both `foo` and `foobar`.
<br>
### Advanced globbing
* [extglobs](#extglobs)
* [POSIX brackets](#posix-brackets)
* [Braces](#brace-expansion)
#### Extglobs
| **Pattern** | **Description** |
| --- | --- |
| `@(pattern)` | Match _only one_ consecutive occurrence of `pattern` |
| `*(pattern)` | Match _zero or more_ consecutive occurrences of `pattern` |
| `+(pattern)` | Match _one or more_ consecutive occurrences of `pattern` |
| `?(pattern)` | Match _zero or **one**_ consecutive occurrences of `pattern` |
| `!(pattern)` | Match _anything but_ `pattern` |
**Examples**
```js
const pm = require('picomatch');
// *(pattern) matches ZERO or more of "pattern"
console.log(pm.isMatch('a', 'a*(z)')); // true
console.log(pm.isMatch('az', 'a*(z)')); // true
console.log(pm.isMatch('azzz', 'a*(z)')); // true
// +(pattern) matches ONE or more of "pattern"
console.log(pm.isMatch('a', 'a*(z)')); // true
console.log(pm.isMatch('az', 'a*(z)')); // true
console.log(pm.isMatch('azzz', 'a*(z)')); // true
// supports multiple extglobs
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // false
// supports nested extglobs
console.log(pm.isMatch('foo.bar', '!(!(foo)).!(!(bar))')); // true
```
#### POSIX brackets
POSIX classes are disabled by default. Enable this feature by setting the `posix` option to true.
**Enable POSIX bracket support**
```js
console.log(pm.makeRe('[[:word:]]+', { posix: true }));
//=> /^(?:(?=.)[A-Za-z0-9_]+\/?)$/
```
**Supported POSIX classes**
The following named POSIX bracket expressions are supported:
* `[:alnum:]` - Alphanumeric characters, equ `[a-zA-Z0-9]`
* `[:alpha:]` - Alphabetical characters, equivalent to `[a-zA-Z]`.
* `[:ascii:]` - ASCII characters, equivalent to `[\\x00-\\x7F]`.
* `[:blank:]` - Space and tab characters, equivalent to `[ \\t]`.
* `[:cntrl:]` - Control characters, equivalent to `[\\x00-\\x1F\\x7F]`.
* `[:digit:]` - Numerical digits, equivalent to `[0-9]`.
* `[:graph:]` - Graph characters, equivalent to `[\\x21-\\x7E]`.
* `[:lower:]` - Lowercase letters, equivalent to `[a-z]`.
* `[:print:]` - Print characters, equivalent to `[\\x20-\\x7E ]`.
* `[:punct:]` - Punctuation and symbols, equivalent to `[\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~]`.
* `[:space:]` - Extended space characters, equivalent to `[ \\t\\r\\n\\v\\f]`.
* `[:upper:]` - Uppercase letters, equivalent to `[A-Z]`.
* `[:word:]` - Word characters (letters, numbers and underscores), equivalent to `[A-Za-z0-9_]`.
* `[:xdigit:]` - Hexadecimal digits, equivalent to `[A-Fa-f0-9]`.
See the [Bash Reference Manual](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html) for more information.
### Braces
Picomatch does not do brace expansion. For [brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html) and advanced matching with braces, use [micromatch](https://github.com/micromatch/micromatch) instead. Picomatch has very basic support for braces.
### Matching special characters as literals
If you wish to match the following special characters in a filepath, and you want to use these characters in your glob pattern, they must be escaped with backslashes or quotes:
**Special Characters**
Some characters that are used for matching in regular expressions are also regarded as valid file path characters on some platforms.
To match any of the following characters as literals: `$^*+?()[]
Examples:
```js
console.log(pm.makeRe('foo/bar \\(1\\)'));
console.log(pm.makeRe('foo/bar \\(1\\)'));
```
<br>
<br>
## Library Comparisons
The following table shows which features are supported by [minimatch](https://github.com/isaacs/minimatch), [micromatch](https://github.com/micromatch/micromatch), [picomatch](https://github.com/micromatch/picomatch), [nanomatch](https://github.com/micromatch/nanomatch), [extglob](https://github.com/micromatch/extglob), [braces](https://github.com/micromatch/braces), and [expand-brackets](https://github.com/micromatch/expand-brackets).
| **Feature** | `minimatch` | `micromatch` | `picomatch` | `nanomatch` | `extglob` | `braces` | `expand-brackets` |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Wildcard matching (`*?+`) | ✔ | ✔ | ✔ | ✔ | - | - | - |
| Advancing globbing | ✔ | ✔ | ✔ | - | - | - | - |
| Brace _matching_ | ✔ | ✔ | ✔ | - | - | ✔ | - |
| Brace _expansion_ | ✔ | ✔ | - | - | - | ✔ | - |
| Extglobs | partial | ✔ | ✔ | - | ✔ | - | - |
| Posix brackets | - | ✔ | ✔ | - | - | - | ✔ |
| Regular expression syntax | - | ✔ | ✔ | ✔ | ✔ | - | ✔ |
| File system operations | - | - | - | - | - | - | - |
<br>
<br>
## Benchmarks
Performance comparison of picomatch and minimatch.
```
# .makeRe star
picomatch x 1,993,050 ops/sec ±0.51% (91 runs sampled)
minimatch x 627,206 ops/sec ±1.96% (87 runs sampled))
# .makeRe star; dot=true
picomatch x 1,436,640 ops/sec ±0.62% (91 runs sampled)
minimatch x 525,876 ops/sec ±0.60% (88 runs sampled)
# .makeRe globstar
picomatch x 1,592,742 ops/sec ±0.42% (90 runs sampled)
minimatch x 962,043 ops/sec ±1.76% (91 runs sampled)d)
# .makeRe globstars
picomatch x 1,615,199 ops/sec ±0.35% (94 runs sampled)
minimatch x 477,179 ops/sec ±1.33% (91 runs sampled)
# .makeRe with leading star
picomatch x 1,220,856 ops/sec ±0.40% (92 runs sampled)
minimatch x 453,564 ops/sec ±1.43% (94 runs sampled)
# .makeRe - basic braces
picomatch x 392,067 ops/sec ±0.70% (90 runs sampled)
minimatch x 99,532 ops/sec ±2.03% (87 runs sampled))
```
<br>
<br>
## Philosophies
The goal of this library is to be blazing fast, without compromising on accuracy.
**Accuracy**
The number one of goal of this library is accuracy. However, it's not unusual for different glob implementations to have different rules for matching behavior, even with simple wildcard matching. It gets increasingly more complicated when combinations of different features are combined, like when extglobs are combined with globstars, braces, slashes, and so on: `!(**/{a,b,*/c})`.
Thus, given that there is no canonical glob specification to use as a single source of truth when differences of opinion arise regarding behavior, sometimes we have to implement our best judgement and rely on feedback from users to make improvements.
**Performance**
Although this library performs well in benchmarks, and in most cases it's faster than other popular libraries we benchmarked against, we will always choose accuracy over performance. It's not helpful to anyone if our library is faster at returning the wrong answer.
<br>
<br>
## About
<details>
<summary><strong>Contributing</strong></summary>
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
Please read the [contributing guide](.github/contributing.md) for advice on opening issues, pull requests, and coding standards.
</details>
<details>
<summary><strong>Running Tests</strong></summary>
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
npm install && npm test
```
</details>
<details>
<summary><strong>Building docs</strong></summary>
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
npm install -g verbose/verb#dev verb-generate-readme && verb
```
</details>
### Author
**Jon Schlinkert**
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
### License
Copyright © 2017-present, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).

View File

@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/picomatch');

View File

@@ -0,0 +1,179 @@
'use strict';
const path = require('path');
const WIN_SLASH = '\\\\/';
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
/**
* Posix glob regex
*/
const DOT_LITERAL = '\\.';
const PLUS_LITERAL = '\\+';
const QMARK_LITERAL = '\\?';
const SLASH_LITERAL = '\\/';
const ONE_CHAR = '(?=.)';
const QMARK = '[^/]';
const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`;
const START_ANCHOR = `(?:^|${SLASH_LITERAL})`;
const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`;
const NO_DOT = `(?!${DOT_LITERAL})`;
const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`;
const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`;
const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`;
const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`;
const STAR = `${QMARK}*?`;
const POSIX_CHARS = {
DOT_LITERAL,
PLUS_LITERAL,
QMARK_LITERAL,
SLASH_LITERAL,
ONE_CHAR,
QMARK,
END_ANCHOR,
DOTS_SLASH,
NO_DOT,
NO_DOTS,
NO_DOT_SLASH,
NO_DOTS_SLASH,
QMARK_NO_DOT,
STAR,
START_ANCHOR
};
/**
* Windows glob regex
*/
const WINDOWS_CHARS = {
...POSIX_CHARS,
SLASH_LITERAL: `[${WIN_SLASH}]`,
QMARK: WIN_NO_SLASH,
STAR: `${WIN_NO_SLASH}*?`,
DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`,
NO_DOT: `(?!${DOT_LITERAL})`,
NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`,
NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
QMARK_NO_DOT: `[^.${WIN_SLASH}]`,
START_ANCHOR: `(?:^|[${WIN_SLASH}])`,
END_ANCHOR: `(?:[${WIN_SLASH}]|$)`
};
/**
* POSIX Bracket Regex
*/
const POSIX_REGEX_SOURCE = {
alnum: 'a-zA-Z0-9',
alpha: 'a-zA-Z',
ascii: '\\x00-\\x7F',
blank: ' \\t',
cntrl: '\\x00-\\x1F\\x7F',
digit: '0-9',
graph: '\\x21-\\x7E',
lower: 'a-z',
print: '\\x20-\\x7E ',
punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~',
space: ' \\t\\r\\n\\v\\f',
upper: 'A-Z',
word: 'A-Za-z0-9_',
xdigit: 'A-Fa-f0-9'
};
module.exports = {
MAX_LENGTH: 1024 * 64,
POSIX_REGEX_SOURCE,
// regular expressions
REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g,
REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/,
REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/,
REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g,
REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g,
REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g,
// Replace globs with equivalent patterns to reduce parsing time.
REPLACEMENTS: {
'***': '*',
'**/**': '**',
'**/**/**': '**'
},
// Digits
CHAR_0: 48, /* 0 */
CHAR_9: 57, /* 9 */
// Alphabet chars.
CHAR_UPPERCASE_A: 65, /* A */
CHAR_LOWERCASE_A: 97, /* a */
CHAR_UPPERCASE_Z: 90, /* Z */
CHAR_LOWERCASE_Z: 122, /* z */
CHAR_LEFT_PARENTHESES: 40, /* ( */
CHAR_RIGHT_PARENTHESES: 41, /* ) */
CHAR_ASTERISK: 42, /* * */
// Non-alphabetic chars.
CHAR_AMPERSAND: 38, /* & */
CHAR_AT: 64, /* @ */
CHAR_BACKWARD_SLASH: 92, /* \ */
CHAR_CARRIAGE_RETURN: 13, /* \r */
CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */
CHAR_COLON: 58, /* : */
CHAR_COMMA: 44, /* , */
CHAR_DOT: 46, /* . */
CHAR_DOUBLE_QUOTE: 34, /* " */
CHAR_EQUAL: 61, /* = */
CHAR_EXCLAMATION_MARK: 33, /* ! */
CHAR_FORM_FEED: 12, /* \f */
CHAR_FORWARD_SLASH: 47, /* / */
CHAR_GRAVE_ACCENT: 96, /* ` */
CHAR_HASH: 35, /* # */
CHAR_HYPHEN_MINUS: 45, /* - */
CHAR_LEFT_ANGLE_BRACKET: 60, /* < */
CHAR_LEFT_CURLY_BRACE: 123, /* { */
CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */
CHAR_LINE_FEED: 10, /* \n */
CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */
CHAR_PERCENT: 37, /* % */
CHAR_PLUS: 43, /* + */
CHAR_QUESTION_MARK: 63, /* ? */
CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */
CHAR_RIGHT_CURLY_BRACE: 125, /* } */
CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */
CHAR_SEMICOLON: 59, /* ; */
CHAR_SINGLE_QUOTE: 39, /* ' */
CHAR_SPACE: 32, /* */
CHAR_TAB: 9, /* \t */
CHAR_UNDERSCORE: 95, /* _ */
CHAR_VERTICAL_LINE: 124, /* | */
CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */
SEP: path.sep,
/**
* Create EXTGLOB_CHARS
*/
extglobChars(chars) {
return {
'!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` },
'?': { type: 'qmark', open: '(?:', close: ')?' },
'+': { type: 'plus', open: '(?:', close: ')+' },
'*': { type: 'star', open: '(?:', close: ')*' },
'@': { type: 'at', open: '(?:', close: ')' }
};
},
/**
* Create GLOB_CHARS
*/
globChars(win32) {
return win32 === true ? WINDOWS_CHARS : POSIX_CHARS;
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,342 @@
'use strict';
const path = require('path');
const scan = require('./scan');
const parse = require('./parse');
const utils = require('./utils');
const constants = require('./constants');
const isObject = val => val && typeof val === 'object' && !Array.isArray(val);
/**
* Creates a matcher function from one or more glob patterns. The
* returned function takes a string to match as its first argument,
* and returns true if the string is a match. The returned matcher
* function also takes a boolean as the second argument that, when true,
* returns an object with additional information.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch(glob[, options]);
*
* const isMatch = picomatch('*.!(*a)');
* console.log(isMatch('a.a')); //=> false
* console.log(isMatch('a.b')); //=> true
* ```
* @name picomatch
* @param {String|Array} `globs` One or more glob patterns.
* @param {Object=} `options`
* @return {Function=} Returns a matcher function.
* @api public
*/
const picomatch = (glob, options, returnState = false) => {
if (Array.isArray(glob)) {
const fns = glob.map(input => picomatch(input, options, returnState));
const arrayMatcher = str => {
for (const isMatch of fns) {
const state = isMatch(str);
if (state) return state;
}
return false;
};
return arrayMatcher;
}
const isState = isObject(glob) && glob.tokens && glob.input;
if (glob === '' || (typeof glob !== 'string' && !isState)) {
throw new TypeError('Expected pattern to be a non-empty string');
}
const opts = options || {};
const posix = utils.isWindows(options);
const regex = isState
? picomatch.compileRe(glob, options)
: picomatch.makeRe(glob, options, false, true);
const state = regex.state;
delete regex.state;
let isIgnored = () => false;
if (opts.ignore) {
const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null };
isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
}
const matcher = (input, returnObject = false) => {
const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix });
const result = { glob, state, regex, posix, input, output, match, isMatch };
if (typeof opts.onResult === 'function') {
opts.onResult(result);
}
if (isMatch === false) {
result.isMatch = false;
return returnObject ? result : false;
}
if (isIgnored(input)) {
if (typeof opts.onIgnore === 'function') {
opts.onIgnore(result);
}
result.isMatch = false;
return returnObject ? result : false;
}
if (typeof opts.onMatch === 'function') {
opts.onMatch(result);
}
return returnObject ? result : true;
};
if (returnState) {
matcher.state = state;
}
return matcher;
};
/**
* Test `input` with the given `regex`. This is used by the main
* `picomatch()` function to test the input string.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.test(input, regex[, options]);
*
* console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
* // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }
* ```
* @param {String} `input` String to test.
* @param {RegExp} `regex`
* @return {Object} Returns an object with matching info.
* @api public
*/
picomatch.test = (input, regex, options, { glob, posix } = {}) => {
if (typeof input !== 'string') {
throw new TypeError('Expected input to be a string');
}
if (input === '') {
return { isMatch: false, output: '' };
}
const opts = options || {};
const format = opts.format || (posix ? utils.toPosixSlashes : null);
let match = input === glob;
let output = (match && format) ? format(input) : input;
if (match === false) {
output = format ? format(input) : input;
match = output === glob;
}
if (match === false || opts.capture === true) {
if (opts.matchBase === true || opts.basename === true) {
match = picomatch.matchBase(input, regex, options, posix);
} else {
match = regex.exec(output);
}
}
return { isMatch: Boolean(match), match, output };
};
/**
* Match the basename of a filepath.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.matchBase(input, glob[, options]);
* console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
* ```
* @param {String} `input` String to test.
* @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe).
* @return {Boolean}
* @api public
*/
picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => {
const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options);
return regex.test(path.basename(input));
};
/**
* Returns true if **any** of the given glob `patterns` match the specified `string`.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.isMatch(string, patterns[, options]);
*
* console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
* console.log(picomatch.isMatch('a.a', 'b.*')); //=> false
* ```
* @param {String|Array} str The string to test.
* @param {String|Array} patterns One or more glob patterns to use for matching.
* @param {Object} [options] See available [options](#options).
* @return {Boolean} Returns true if any patterns match `str`
* @api public
*/
picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
/**
* Parse a glob pattern to create the source string for a regular
* expression.
*
* ```js
* const picomatch = require('picomatch');
* const result = picomatch.parse(pattern[, options]);
* ```
* @param {String} `pattern`
* @param {Object} `options`
* @return {Object} Returns an object with useful properties and output to be used as a regex source string.
* @api public
*/
picomatch.parse = (pattern, options) => {
if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options));
return parse(pattern, { ...options, fastpaths: false });
};
/**
* Scan a glob pattern to separate the pattern into segments.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.scan(input[, options]);
*
* const result = picomatch.scan('!./foo/*.js');
* console.log(result);
* { prefix: '!./',
* input: '!./foo/*.js',
* start: 3,
* base: 'foo',
* glob: '*.js',
* isBrace: false,
* isBracket: false,
* isGlob: true,
* isExtglob: false,
* isGlobstar: false,
* negated: true }
* ```
* @param {String} `input` Glob pattern to scan.
* @param {Object} `options`
* @return {Object} Returns an object with
* @api public
*/
picomatch.scan = (input, options) => scan(input, options);
/**
* Compile a regular expression from the `state` object returned by the
* [parse()](#parse) method.
*
* @param {Object} `state`
* @param {Object} `options`
* @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser.
* @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging.
* @return {RegExp}
* @api public
*/
picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => {
if (returnOutput === true) {
return state.output;
}
const opts = options || {};
const prepend = opts.contains ? '' : '^';
const append = opts.contains ? '' : '$';
let source = `${prepend}(?:${state.output})${append}`;
if (state && state.negated === true) {
source = `^(?!${source}).*$`;
}
const regex = picomatch.toRegex(source, options);
if (returnState === true) {
regex.state = state;
}
return regex;
};
/**
* Create a regular expression from a parsed glob pattern.
*
* ```js
* const picomatch = require('picomatch');
* const state = picomatch.parse('*.js');
* // picomatch.compileRe(state[, options]);
*
* console.log(picomatch.compileRe(state));
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
* ```
* @param {String} `state` The object returned from the `.parse` method.
* @param {Object} `options`
* @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result.
* @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression.
* @return {RegExp} Returns a regex created from the given pattern.
* @api public
*/
picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => {
if (!input || typeof input !== 'string') {
throw new TypeError('Expected a non-empty string');
}
let parsed = { negated: false, fastpaths: true };
if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) {
parsed.output = parse.fastpaths(input, options);
}
if (!parsed.output) {
parsed = parse(input, options);
}
return picomatch.compileRe(parsed, options, returnOutput, returnState);
};
/**
* Create a regular expression from the given regex source string.
*
* ```js
* const picomatch = require('picomatch');
* // picomatch.toRegex(source[, options]);
*
* const { output } = picomatch.parse('*.js');
* console.log(picomatch.toRegex(output));
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
* ```
* @param {String} `source` Regular expression source string.
* @param {Object} `options`
* @return {RegExp}
* @api public
*/
picomatch.toRegex = (source, options) => {
try {
const opts = options || {};
return new RegExp(source, opts.flags || (opts.nocase ? 'i' : ''));
} catch (err) {
if (options && options.debug === true) throw err;
return /$^/;
}
};
/**
* Picomatch constants.
* @return {Object}
*/
picomatch.constants = constants;
/**
* Expose "picomatch"
*/
module.exports = picomatch;

View File

@@ -0,0 +1,391 @@
'use strict';
const utils = require('./utils');
const {
CHAR_ASTERISK, /* * */
CHAR_AT, /* @ */
CHAR_BACKWARD_SLASH, /* \ */
CHAR_COMMA, /* , */
CHAR_DOT, /* . */
CHAR_EXCLAMATION_MARK, /* ! */
CHAR_FORWARD_SLASH, /* / */
CHAR_LEFT_CURLY_BRACE, /* { */
CHAR_LEFT_PARENTHESES, /* ( */
CHAR_LEFT_SQUARE_BRACKET, /* [ */
CHAR_PLUS, /* + */
CHAR_QUESTION_MARK, /* ? */
CHAR_RIGHT_CURLY_BRACE, /* } */
CHAR_RIGHT_PARENTHESES, /* ) */
CHAR_RIGHT_SQUARE_BRACKET /* ] */
} = require('./constants');
const isPathSeparator = code => {
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
};
const depth = token => {
if (token.isPrefix !== true) {
token.depth = token.isGlobstar ? Infinity : 1;
}
};
/**
* Quickly scans a glob pattern and returns an object with a handful of
* useful properties, like `isGlob`, `path` (the leading non-glob, if it exists),
* `glob` (the actual pattern), `negated` (true if the path starts with `!` but not
* with `!(`) and `negatedExtglob` (true if the path starts with `!(`).
*
* ```js
* const pm = require('picomatch');
* console.log(pm.scan('foo/bar/*.js'));
* { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' }
* ```
* @param {String} `str`
* @param {Object} `options`
* @return {Object} Returns an object with tokens and regex source string.
* @api public
*/
const scan = (input, options) => {
const opts = options || {};
const length = input.length - 1;
const scanToEnd = opts.parts === true || opts.scanToEnd === true;
const slashes = [];
const tokens = [];
const parts = [];
let str = input;
let index = -1;
let start = 0;
let lastIndex = 0;
let isBrace = false;
let isBracket = false;
let isGlob = false;
let isExtglob = false;
let isGlobstar = false;
let braceEscaped = false;
let backslashes = false;
let negated = false;
let negatedExtglob = false;
let finished = false;
let braces = 0;
let prev;
let code;
let token = { value: '', depth: 0, isGlob: false };
const eos = () => index >= length;
const peek = () => str.charCodeAt(index + 1);
const advance = () => {
prev = code;
return str.charCodeAt(++index);
};
while (index < length) {
code = advance();
let next;
if (code === CHAR_BACKWARD_SLASH) {
backslashes = token.backslashes = true;
code = advance();
if (code === CHAR_LEFT_CURLY_BRACE) {
braceEscaped = true;
}
continue;
}
if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) {
braces++;
while (eos() !== true && (code = advance())) {
if (code === CHAR_BACKWARD_SLASH) {
backslashes = token.backslashes = true;
advance();
continue;
}
if (code === CHAR_LEFT_CURLY_BRACE) {
braces++;
continue;
}
if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) {
isBrace = token.isBrace = true;
isGlob = token.isGlob = true;
finished = true;
if (scanToEnd === true) {
continue;
}
break;
}
if (braceEscaped !== true && code === CHAR_COMMA) {
isBrace = token.isBrace = true;
isGlob = token.isGlob = true;
finished = true;
if (scanToEnd === true) {
continue;
}
break;
}
if (code === CHAR_RIGHT_CURLY_BRACE) {
braces--;
if (braces === 0) {
braceEscaped = false;
isBrace = token.isBrace = true;
finished = true;
break;
}
}
}
if (scanToEnd === true) {
continue;
}
break;
}
if (code === CHAR_FORWARD_SLASH) {
slashes.push(index);
tokens.push(token);
token = { value: '', depth: 0, isGlob: false };
if (finished === true) continue;
if (prev === CHAR_DOT && index === (start + 1)) {
start += 2;
continue;
}
lastIndex = index + 1;
continue;
}
if (opts.noext !== true) {
const isExtglobChar = code === CHAR_PLUS
|| code === CHAR_AT
|| code === CHAR_ASTERISK
|| code === CHAR_QUESTION_MARK
|| code === CHAR_EXCLAMATION_MARK;
if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) {
isGlob = token.isGlob = true;
isExtglob = token.isExtglob = true;
finished = true;
if (code === CHAR_EXCLAMATION_MARK && index === start) {
negatedExtglob = true;
}
if (scanToEnd === true) {
while (eos() !== true && (code = advance())) {
if (code === CHAR_BACKWARD_SLASH) {
backslashes = token.backslashes = true;
code = advance();
continue;
}
if (code === CHAR_RIGHT_PARENTHESES) {
isGlob = token.isGlob = true;
finished = true;
break;
}
}
continue;
}
break;
}
}
if (code === CHAR_ASTERISK) {
if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true;
isGlob = token.isGlob = true;
finished = true;
if (scanToEnd === true) {
continue;
}
break;
}
if (code === CHAR_QUESTION_MARK) {
isGlob = token.isGlob = true;
finished = true;
if (scanToEnd === true) {
continue;
}
break;
}
if (code === CHAR_LEFT_SQUARE_BRACKET) {
while (eos() !== true && (next = advance())) {
if (next === CHAR_BACKWARD_SLASH) {
backslashes = token.backslashes = true;
advance();
continue;
}
if (next === CHAR_RIGHT_SQUARE_BRACKET) {
isBracket = token.isBracket = true;
isGlob = token.isGlob = true;
finished = true;
break;
}
}
if (scanToEnd === true) {
continue;
}
break;
}
if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) {
negated = token.negated = true;
start++;
continue;
}
if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) {
isGlob = token.isGlob = true;
if (scanToEnd === true) {
while (eos() !== true && (code = advance())) {
if (code === CHAR_LEFT_PARENTHESES) {
backslashes = token.backslashes = true;
code = advance();
continue;
}
if (code === CHAR_RIGHT_PARENTHESES) {
finished = true;
break;
}
}
continue;
}
break;
}
if (isGlob === true) {
finished = true;
if (scanToEnd === true) {
continue;
}
break;
}
}
if (opts.noext === true) {
isExtglob = false;
isGlob = false;
}
let base = str;
let prefix = '';
let glob = '';
if (start > 0) {
prefix = str.slice(0, start);
str = str.slice(start);
lastIndex -= start;
}
if (base && isGlob === true && lastIndex > 0) {
base = str.slice(0, lastIndex);
glob = str.slice(lastIndex);
} else if (isGlob === true) {
base = '';
glob = str;
} else {
base = str;
}
if (base && base !== '' && base !== '/' && base !== str) {
if (isPathSeparator(base.charCodeAt(base.length - 1))) {
base = base.slice(0, -1);
}
}
if (opts.unescape === true) {
if (glob) glob = utils.removeBackslashes(glob);
if (base && backslashes === true) {
base = utils.removeBackslashes(base);
}
}
const state = {
prefix,
input,
start,
base,
glob,
isBrace,
isBracket,
isGlob,
isExtglob,
isGlobstar,
negated,
negatedExtglob
};
if (opts.tokens === true) {
state.maxDepth = 0;
if (!isPathSeparator(code)) {
tokens.push(token);
}
state.tokens = tokens;
}
if (opts.parts === true || opts.tokens === true) {
let prevIndex;
for (let idx = 0; idx < slashes.length; idx++) {
const n = prevIndex ? prevIndex + 1 : start;
const i = slashes[idx];
const value = input.slice(n, i);
if (opts.tokens) {
if (idx === 0 && start !== 0) {
tokens[idx].isPrefix = true;
tokens[idx].value = prefix;
} else {
tokens[idx].value = value;
}
depth(tokens[idx]);
state.maxDepth += tokens[idx].depth;
}
if (idx !== 0 || value !== '') {
parts.push(value);
}
prevIndex = i;
}
if (prevIndex && prevIndex + 1 < input.length) {
const value = input.slice(prevIndex + 1);
parts.push(value);
if (opts.tokens) {
tokens[tokens.length - 1].value = value;
depth(tokens[tokens.length - 1]);
state.maxDepth += tokens[tokens.length - 1].depth;
}
}
state.slashes = slashes;
state.parts = parts;
}
return state;
};
module.exports = scan;

View File

@@ -0,0 +1,64 @@
'use strict';
const path = require('path');
const win32 = process.platform === 'win32';
const {
REGEX_BACKSLASH,
REGEX_REMOVE_BACKSLASH,
REGEX_SPECIAL_CHARS,
REGEX_SPECIAL_CHARS_GLOBAL
} = require('./constants');
exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str);
exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str);
exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1');
exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/');
exports.removeBackslashes = str => {
return str.replace(REGEX_REMOVE_BACKSLASH, match => {
return match === '\\' ? '' : match;
});
};
exports.supportsLookbehinds = () => {
const segs = process.version.slice(1).split('.').map(Number);
if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) {
return true;
}
return false;
};
exports.isWindows = options => {
if (options && typeof options.windows === 'boolean') {
return options.windows;
}
return win32 === true || path.sep === '\\';
};
exports.escapeLast = (input, char, lastIdx) => {
const idx = input.lastIndexOf(char, lastIdx);
if (idx === -1) return input;
if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1);
return `${input.slice(0, idx)}\\${input.slice(idx)}`;
};
exports.removePrefix = (input, state = {}) => {
let output = input;
if (output.startsWith('./')) {
output = output.slice(2);
state.prefix = './';
}
return output;
};
exports.wrapOutput = (input, state = {}, options = {}) => {
const prepend = options.contains ? '' : '^';
const append = options.contains ? '' : '$';
let output = `${prepend}(?:${input})${append}`;
if (state.negated === true) {
output = `(?:^(?!${output}).*$)`;
}
return output;
};

View File

@@ -0,0 +1,81 @@
{
"name": "picomatch",
"description": "Blazing fast and accurate glob matcher written in JavaScript, with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions.",
"version": "2.3.1",
"homepage": "https://github.com/micromatch/picomatch",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"funding": "https://github.com/sponsors/jonschlinkert",
"repository": "micromatch/picomatch",
"bugs": {
"url": "https://github.com/micromatch/picomatch/issues"
},
"license": "MIT",
"files": [
"index.js",
"lib"
],
"main": "index.js",
"engines": {
"node": ">=8.6"
},
"scripts": {
"lint": "eslint --cache --cache-location node_modules/.cache/.eslintcache --report-unused-disable-directives --ignore-path .gitignore .",
"mocha": "mocha --reporter dot",
"test": "npm run lint && npm run mocha",
"test:ci": "npm run test:cover",
"test:cover": "nyc npm run mocha"
},
"devDependencies": {
"eslint": "^6.8.0",
"fill-range": "^7.0.1",
"gulp-format-md": "^2.0.0",
"mocha": "^6.2.2",
"nyc": "^15.0.0",
"time-require": "github:jonschlinkert/time-require"
},
"keywords": [
"glob",
"match",
"picomatch"
],
"nyc": {
"reporter": [
"html",
"lcov",
"text-summary"
]
},
"verb": {
"toc": {
"render": true,
"method": "preWrite",
"maxdepth": 3
},
"layout": "empty",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"lint": {
"reflinks": true
},
"related": {
"list": [
"braces",
"micromatch"
]
},
"reflinks": [
"braces",
"expand-brackets",
"extglob",
"fill-range",
"micromatch",
"minimatch",
"nanomatch",
"picomatch"
]
}
}

View File

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

View File

@@ -0,0 +1,122 @@
# readdirp [![Weekly downloads](https://img.shields.io/npm/dw/readdirp.svg)](https://github.com/paulmillr/readdirp)
Recursive version of [fs.readdir](https://nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback). Exposes a **stream API** and a **promise API**.
```sh
npm install readdirp
```
```javascript
const readdirp = require('readdirp');
// Use streams to achieve small RAM & CPU footprint.
// 1) Streams example with for-await.
for await (const entry of readdirp('.')) {
const {path} = entry;
console.log(`${JSON.stringify({path})}`);
}
// 2) Streams example, non for-await.
// Print out all JS files along with their size within the current folder & subfolders.
readdirp('.', {fileFilter: '*.js', alwaysStat: true})
.on('data', (entry) => {
const {path, stats: {size}} = entry;
console.log(`${JSON.stringify({path, size})}`);
})
// Optionally call stream.destroy() in `warn()` in order to abort and cause 'close' to be emitted
.on('warn', error => console.error('non-fatal error', error))
.on('error', error => console.error('fatal error', error))
.on('end', () => console.log('done'));
// 3) Promise example. More RAM and CPU than streams / for-await.
const files = await readdirp.promise('.');
console.log(files.map(file => file.path));
// Other options.
readdirp('test', {
fileFilter: '*.js',
directoryFilter: ['!.git', '!*modules']
// directoryFilter: (di) => di.basename.length === 9
type: 'files_directories',
depth: 1
});
```
For more examples, check out `examples` directory.
## API
`const stream = readdirp(root[, options])`**Stream API**
- Reads given root recursively and returns a `stream` of [entry infos](#entryinfo)
- Optionally can be used like `for await (const entry of stream)` with node.js 10+ (`asyncIterator`).
- `on('data', (entry) => {})` [entry info](#entryinfo) for every file / dir.
- `on('warn', (error) => {})` non-fatal `Error` that prevents a file / dir from being processed. Example: inaccessible to the user.
- `on('error', (error) => {})` fatal `Error` which also ends the stream. Example: illegal options where passed.
- `on('end')` — we are done. Called when all entries were found and no more will be emitted.
- `on('close')` — stream is destroyed via `stream.destroy()`.
Could be useful if you want to manually abort even on a non fatal error.
At that point the stream is no longer `readable` and no more entries, warning or errors are emitted
- To learn more about streams, consult the very detailed [nodejs streams documentation](https://nodejs.org/api/stream.html)
or the [stream-handbook](https://github.com/substack/stream-handbook)
`const entries = await readdirp.promise(root[, options])`**Promise API**. Returns a list of [entry infos](#entryinfo).
First argument is awalys `root`, path in which to start reading and recursing into subdirectories.
### options
- `fileFilter: ["*.js"]`: filter to include or exclude files. A `Function`, Glob string or Array of glob strings.
- **Function**: a function that takes an entry info as a parameter and returns true to include or false to exclude the entry
- **Glob string**: a string (e.g., `*.js`) which is matched using [picomatch](https://github.com/micromatch/picomatch), so go there for more
information. Globstars (`**`) are not supported since specifying a recursive pattern for an already recursive function doesn't make sense. Negated globs (as explained in the minimatch documentation) are allowed, e.g., `!*.txt` matches everything but text files.
- **Array of glob strings**: either need to be all inclusive or all exclusive (negated) patterns otherwise an error is thrown.
`['*.json', '*.js']` includes all JavaScript and Json files.
`['!.git', '!node_modules']` includes all directories except the '.git' and 'node_modules'.
- Directories that do not pass a filter will not be recursed into.
- `directoryFilter: ['!.git']`: filter to include/exclude directories found and to recurse into. Directories that do not pass a filter will not be recursed into.
- `depth: 5`: depth at which to stop recursing even if more subdirectories are found
- `type: 'files'`: determines if data events on the stream should be emitted for `'files'` (default), `'directories'`, `'files_directories'`, or `'all'`. Setting to `'all'` will also include entries for other types of file descriptors like character devices, unix sockets and named pipes.
- `alwaysStat: false`: always return `stats` property for every file. Default is `false`, readdirp will return `Dirent` entries. Setting it to `true` can double readdir execution time - use it only when you need file `size`, `mtime` etc. Cannot be enabled on node <10.10.0.
- `lstat: false`: include symlink entries in the stream along with files. When `true`, `fs.lstat` would be used instead of `fs.stat`
### `EntryInfo`
Has the following properties:
- `path: 'assets/javascripts/react.js'`: path to the file/directory (relative to given root)
- `fullPath: '/Users/dev/projects/app/assets/javascripts/react.js'`: full path to the file/directory found
- `basename: 'react.js'`: name of the file/directory
- `dirent: fs.Dirent`: built-in [dir entry object](https://nodejs.org/api/fs.html#fs_class_fs_dirent) - only with `alwaysStat: false`
- `stats: fs.Stats`: built in [stat object](https://nodejs.org/api/fs.html#fs_class_fs_stats) - only with `alwaysStat: true`
## Changelog
- 3.5 (Oct 13, 2020) disallows recursive directory-based symlinks.
Before, it could have entered infinite loop.
- 3.4 (Mar 19, 2020) adds support for directory-based symlinks.
- 3.3 (Dec 6, 2019) stabilizes RAM consumption and enables perf management with `highWaterMark` option. Fixes race conditions related to `for-await` looping.
- 3.2 (Oct 14, 2019) improves performance by 250% and makes streams implementation more idiomatic.
- 3.1 (Jul 7, 2019) brings `bigint` support to `stat` output on Windows. This is backwards-incompatible for some cases. Be careful. It you use it incorrectly, you'll see "TypeError: Cannot mix BigInt and other types, use explicit conversions".
- 3.0 brings huge performance improvements and stream backpressure support.
- Upgrading 2.x to 3.x:
- Signature changed from `readdirp(options)` to `readdirp(root, options)`
- Replaced callback API with promise API.
- Renamed `entryType` option to `type`
- Renamed `entryType: 'both'` to `'files_directories'`
- `EntryInfo`
- Renamed `stat` to `stats`
- Emitted only when `alwaysStat: true`
- `dirent` is emitted instead of `stats` by default with `alwaysStat: false`
- Renamed `name` to `basename`
- Removed `parentDir` and `fullParentDir` properties
- Supported node.js versions:
- 3.x: node 8+
- 2.x: node 0.6+
## License
Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (<https://paulmillr.com>)
MIT License, see [LICENSE](LICENSE) file.

View File

@@ -0,0 +1,43 @@
// TypeScript Version: 3.2
/// <reference types="node" lib="esnext" />
import * as fs from 'fs';
import { Readable } from 'stream';
declare namespace readdir {
interface EntryInfo {
path: string;
fullPath: string;
basename: string;
stats?: fs.Stats;
dirent?: fs.Dirent;
}
interface ReaddirpOptions {
root?: string;
fileFilter?: string | string[] | ((entry: EntryInfo) => boolean);
directoryFilter?: string | string[] | ((entry: EntryInfo) => boolean);
type?: 'files' | 'directories' | 'files_directories' | 'all';
lstat?: boolean;
depth?: number;
alwaysStat?: boolean;
}
interface ReaddirpStream extends Readable, AsyncIterable<EntryInfo> {
read(): EntryInfo;
[Symbol.asyncIterator](): AsyncIterableIterator<EntryInfo>;
}
function promise(
root: string,
options?: ReaddirpOptions
): Promise<EntryInfo[]>;
}
declare function readdir(
root: string,
options?: readdir.ReaddirpOptions
): readdir.ReaddirpStream;
export = readdir;

View File

@@ -0,0 +1,287 @@
'use strict';
const fs = require('fs');
const { Readable } = require('stream');
const sysPath = require('path');
const { promisify } = require('util');
const picomatch = require('picomatch');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const lstat = promisify(fs.lstat);
const realpath = promisify(fs.realpath);
/**
* @typedef {Object} EntryInfo
* @property {String} path
* @property {String} fullPath
* @property {fs.Stats=} stats
* @property {fs.Dirent=} dirent
* @property {String} basename
*/
const BANG = '!';
const RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';
const NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]);
const FILE_TYPE = 'files';
const DIR_TYPE = 'directories';
const FILE_DIR_TYPE = 'files_directories';
const EVERYTHING_TYPE = 'all';
const ALL_TYPES = [FILE_TYPE, DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE];
const isNormalFlowError = error => NORMAL_FLOW_ERRORS.has(error.code);
const [maj, min] = process.versions.node.split('.').slice(0, 2).map(n => Number.parseInt(n, 10));
const wantBigintFsStats = process.platform === 'win32' && (maj > 10 || (maj === 10 && min >= 5));
const normalizeFilter = filter => {
if (filter === undefined) return;
if (typeof filter === 'function') return filter;
if (typeof filter === 'string') {
const glob = picomatch(filter.trim());
return entry => glob(entry.basename);
}
if (Array.isArray(filter)) {
const positive = [];
const negative = [];
for (const item of filter) {
const trimmed = item.trim();
if (trimmed.charAt(0) === BANG) {
negative.push(picomatch(trimmed.slice(1)));
} else {
positive.push(picomatch(trimmed));
}
}
if (negative.length > 0) {
if (positive.length > 0) {
return entry =>
positive.some(f => f(entry.basename)) && !negative.some(f => f(entry.basename));
}
return entry => !negative.some(f => f(entry.basename));
}
return entry => positive.some(f => f(entry.basename));
}
};
class ReaddirpStream extends Readable {
static get defaultOptions() {
return {
root: '.',
/* eslint-disable no-unused-vars */
fileFilter: (path) => true,
directoryFilter: (path) => true,
/* eslint-enable no-unused-vars */
type: FILE_TYPE,
lstat: false,
depth: 2147483648,
alwaysStat: false
};
}
constructor(options = {}) {
super({
objectMode: true,
autoDestroy: true,
highWaterMark: options.highWaterMark || 4096
});
const opts = { ...ReaddirpStream.defaultOptions, ...options };
const { root, type } = opts;
this._fileFilter = normalizeFilter(opts.fileFilter);
this._directoryFilter = normalizeFilter(opts.directoryFilter);
const statMethod = opts.lstat ? lstat : stat;
// Use bigint stats if it's windows and stat() supports options (node 10+).
if (wantBigintFsStats) {
this._stat = path => statMethod(path, { bigint: true });
} else {
this._stat = statMethod;
}
this._maxDepth = opts.depth;
this._wantsDir = [DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type);
this._wantsFile = [FILE_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type);
this._wantsEverything = type === EVERYTHING_TYPE;
this._root = sysPath.resolve(root);
this._isDirent = ('Dirent' in fs) && !opts.alwaysStat;
this._statsProp = this._isDirent ? 'dirent' : 'stats';
this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };
// Launch stream with one parent, the root dir.
this.parents = [this._exploreDir(root, 1)];
this.reading = false;
this.parent = undefined;
}
async _read(batch) {
if (this.reading) return;
this.reading = true;
try {
while (!this.destroyed && batch > 0) {
const { path, depth, files = [] } = this.parent || {};
if (files.length > 0) {
const slice = files.splice(0, batch).map(dirent => this._formatEntry(dirent, path));
for (const entry of await Promise.all(slice)) {
if (this.destroyed) return;
const entryType = await this._getEntryType(entry);
if (entryType === 'directory' && this._directoryFilter(entry)) {
if (depth <= this._maxDepth) {
this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
}
if (this._wantsDir) {
this.push(entry);
batch--;
}
} else if ((entryType === 'file' || this._includeAsFile(entry)) && this._fileFilter(entry)) {
if (this._wantsFile) {
this.push(entry);
batch--;
}
}
}
} else {
const parent = this.parents.pop();
if (!parent) {
this.push(null);
break;
}
this.parent = await parent;
if (this.destroyed) return;
}
}
} catch (error) {
this.destroy(error);
} finally {
this.reading = false;
}
}
async _exploreDir(path, depth) {
let files;
try {
files = await readdir(path, this._rdOptions);
} catch (error) {
this._onError(error);
}
return { files, depth, path };
}
async _formatEntry(dirent, path) {
let entry;
try {
const basename = this._isDirent ? dirent.name : dirent;
const fullPath = sysPath.resolve(sysPath.join(path, basename));
entry = { path: sysPath.relative(this._root, fullPath), fullPath, basename };
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
} catch (err) {
this._onError(err);
}
return entry;
}
_onError(err) {
if (isNormalFlowError(err) && !this.destroyed) {
this.emit('warn', err);
} else {
this.destroy(err);
}
}
async _getEntryType(entry) {
// entry may be undefined, because a warning or an error were emitted
// and the statsProp is undefined
const stats = entry && entry[this._statsProp];
if (!stats) {
return;
}
if (stats.isFile()) {
return 'file';
}
if (stats.isDirectory()) {
return 'directory';
}
if (stats && stats.isSymbolicLink()) {
const full = entry.fullPath;
try {
const entryRealPath = await realpath(full);
const entryRealPathStats = await lstat(entryRealPath);
if (entryRealPathStats.isFile()) {
return 'file';
}
if (entryRealPathStats.isDirectory()) {
const len = entryRealPath.length;
if (full.startsWith(entryRealPath) && full.substr(len, 1) === sysPath.sep) {
const recursiveError = new Error(
`Circular symlink detected: "${full}" points to "${entryRealPath}"`
);
recursiveError.code = RECURSIVE_ERROR_CODE;
return this._onError(recursiveError);
}
return 'directory';
}
} catch (error) {
this._onError(error);
}
}
}
_includeAsFile(entry) {
const stats = entry && entry[this._statsProp];
return stats && this._wantsEverything && !stats.isDirectory();
}
}
/**
* @typedef {Object} ReaddirpArguments
* @property {Function=} fileFilter
* @property {Function=} directoryFilter
* @property {String=} type
* @property {Number=} depth
* @property {String=} root
* @property {Boolean=} lstat
* @property {Boolean=} bigint
*/
/**
* Main function which ends up calling readdirRec and reads all files and directories in given root recursively.
* @param {String} root Root directory
* @param {ReaddirpArguments=} options Options to specify root (start directory), filters and recursion depth
*/
const readdirp = (root, options = {}) => {
let type = options.entryType || options.type;
if (type === 'both') type = FILE_DIR_TYPE; // backwards-compatibility
if (type) options.type = type;
if (!root) {
throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');
} else if (typeof root !== 'string') {
throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)');
} else if (type && !ALL_TYPES.includes(type)) {
throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);
}
options.root = root;
return new ReaddirpStream(options);
};
const readdirpPromise = (root, options = {}) => {
return new Promise((resolve, reject) => {
const files = [];
readdirp(root, options)
.on('data', entry => files.push(entry))
.on('end', () => resolve(files))
.on('error', error => reject(error));
});
};
readdirp.promise = readdirpPromise;
readdirp.ReaddirpStream = ReaddirpStream;
readdirp.default = readdirp;
module.exports = readdirp;

View File

@@ -0,0 +1,122 @@
{
"name": "readdirp",
"description": "Recursive version of fs.readdir with streaming API.",
"version": "3.6.0",
"homepage": "https://github.com/paulmillr/readdirp",
"repository": {
"type": "git",
"url": "git://github.com/paulmillr/readdirp.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/paulmillr/readdirp/issues"
},
"author": "Thorsten Lorenz <thlorenz@gmx.de> (thlorenz.com)",
"contributors": [
"Thorsten Lorenz <thlorenz@gmx.de> (thlorenz.com)",
"Paul Miller (https://paulmillr.com)"
],
"main": "index.js",
"engines": {
"node": ">=8.10.0"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"recursive",
"fs",
"stream",
"streams",
"readdir",
"filesystem",
"find",
"filter"
],
"scripts": {
"dtslint": "dtslint",
"nyc": "nyc",
"mocha": "mocha --exit",
"lint": "eslint --report-unused-disable-directives --ignore-path .gitignore .",
"test": "npm run lint && nyc npm run mocha"
},
"dependencies": {
"picomatch": "^2.2.1"
},
"devDependencies": {
"@types/node": "^14",
"chai": "^4.2",
"chai-subset": "^1.6",
"dtslint": "^3.3.0",
"eslint": "^7.0.0",
"mocha": "^7.1.1",
"nyc": "^15.0.0",
"rimraf": "^3.0.0",
"typescript": "^4.0.3"
},
"nyc": {
"reporter": [
"html",
"text"
]
},
"eslintConfig": {
"root": true,
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "script"
},
"env": {
"node": true,
"es6": true
},
"rules": {
"array-callback-return": "error",
"no-empty": [
"error",
{
"allowEmptyCatch": true
}
],
"no-else-return": [
"error",
{
"allowElseIf": false
}
],
"no-lonely-if": "error",
"no-var": "error",
"object-shorthand": "error",
"prefer-arrow-callback": [
"error",
{
"allowNamedFunctions": true
}
],
"prefer-const": [
"error",
{
"ignoreReadBeforeAssign": true
}
],
"prefer-destructuring": [
"error",
{
"object": true,
"array": false
}
],
"prefer-spread": "error",
"prefer-template": "error",
"radix": "error",
"semi": "error",
"strict": "error",
"quotes": [
"error",
"single"
]
}
}
}

148
node_modules/webpack-dev-server/package.json generated vendored Normal file
View File

@@ -0,0 +1,148 @@
{
"name": "webpack-dev-server",
"version": "5.2.2",
"description": "Serves a webpack app. Updates the browser on changes.",
"bin": "bin/webpack-dev-server.js",
"main": "lib/Server.js",
"types": "types/lib/Server.d.ts",
"author": "Tobias Koppers @sokra",
"bugs": "https://github.com/webpack/webpack-dev-server/issues",
"homepage": "https://github.com/webpack/webpack-dev-server#readme",
"repository": "https://github.com/webpack/webpack-dev-server",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"files": [
"bin",
"lib",
"client",
"types"
],
"engines": {
"node": ">= 18.12.0"
},
"scripts": {
"fmt:check": "prettier \"{**/*,*}.{js,json,md,yml,css,ts}\" --list-different",
"lint:prettier": "prettier --cache --list-different .",
"lint:js": "eslint --cache .",
"lint:types": "tsc --pretty --noEmit",
"lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"",
"lint": "npm-run-all -l -p \"lint:**\"",
"fix:js": "npm run lint:js -- --fix",
"fix:prettier": "npm run lint:prettier -- --write",
"fix": "npm-run-all -l fix:js fix:prettier",
"commitlint": "commitlint --from=master",
"build:client": "rimraf -g ./client/* && babel client-src/ --out-dir client/ --ignore \"client-src/webpack.config.js\" --ignore \"client-src/modules\" && webpack --config client-src/webpack.config.js",
"build:types": "rimraf -g ./types/* && tsc --declaration --emitDeclarationOnly --outDir types && node ./scripts/extend-webpack-types.js && prettier \"types/**/*.ts\" --write && prettier \"types/**/*.ts\" --write",
"build": "npm-run-all -p \"build:**\"",
"test:only": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"test:coverage": "npm run test:only -- --coverage",
"test:watch": "npm run test:coverage --watch",
"test": "npm run test:coverage",
"pretest": "npm run lint",
"prepare": "husky && npm run build",
"release": "standard-version"
},
"dependencies": {
"@types/bonjour": "^3.5.13",
"@types/connect-history-api-fallback": "^1.5.4",
"@types/express": "^4.17.21",
"@types/express-serve-static-core": "^4.17.21",
"@types/serve-index": "^1.9.4",
"@types/serve-static": "^1.15.5",
"@types/sockjs": "^0.3.36",
"@types/ws": "^8.5.10",
"ansi-html-community": "^0.0.8",
"bonjour-service": "^1.2.1",
"chokidar": "^3.6.0",
"colorette": "^2.0.10",
"compression": "^1.7.4",
"connect-history-api-fallback": "^2.0.0",
"express": "^4.21.2",
"graceful-fs": "^4.2.6",
"http-proxy-middleware": "^2.0.9",
"ipaddr.js": "^2.1.0",
"launch-editor": "^2.6.1",
"open": "^10.0.3",
"p-retry": "^6.2.0",
"schema-utils": "^4.2.0",
"selfsigned": "^2.4.1",
"serve-index": "^1.9.1",
"sockjs": "^0.3.24",
"spdy": "^4.0.2",
"webpack-dev-middleware": "^7.4.2",
"ws": "^8.18.0"
},
"devDependencies": {
"@babel/cli": "^7.25.9",
"@babel/core": "^7.25.9",
"@babel/eslint-parser": "^7.25.9",
"@babel/plugin-transform-object-assign": "^7.25.9",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/preset-env": "^7.25.9",
"@babel/runtime": "^7.25.9",
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@hono/node-server": "^1.13.3",
"@types/compression": "^1.7.2",
"@types/node": "^22.8.4",
"@types/node-forge": "^1.3.1",
"@types/sockjs-client": "^1.5.1",
"@types/trusted-types": "^2.0.2",
"acorn": "^8.14.0",
"babel-jest": "^29.5.0",
"babel-loader": "^10.0.0",
"body-parser": "^1.19.2",
"connect": "^3.7.0",
"core-js": "^3.38.1",
"cspell": "^8.15.5",
"css-loader": "^7.1.1",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-config-webpack": "^1.2.5",
"eslint-plugin-import": "^2.31.0",
"execa": "^5.1.1",
"hono": "^4.6.8",
"html-webpack-plugin": "^5.6.3",
"http-proxy": "^1.18.1",
"husky": "^9.1.6",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"klona": "^2.0.4",
"less": "^4.1.1",
"less-loader": "^12.1.0",
"lint-staged": "^15.2.10",
"marked": "^12.0.0",
"memfs": "^4.14.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.2.4",
"puppeteer": "^24.10.0",
"readable-stream": "^4.5.2",
"require-from-string": "^2.0.2",
"rimraf": "^5.0.5",
"sockjs-client": "^1.6.1",
"standard-version": "^9.3.0",
"strip-ansi-v6": "npm:strip-ansi@^6.0.0",
"style-loader": "^4.0.0",
"supertest": "^7.0.0",
"tcp-port-used": "^1.0.2",
"typescript": "^5.7.2",
"wait-for-expect": "^3.0.2",
"webpack": "^5.94.0",
"webpack-cli": "^5.0.1",
"webpack-merge": "^6.0.1"
},
"peerDependencies": {
"webpack": "^5.0.0"
},
"peerDependenciesMeta": {
"webpack-cli": {
"optional": true
},
"webpack": {
"optional": true
}
}
}

1002
node_modules/webpack-dev-server/types/bin/cli-flags.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env node
export type CliOption = {
/**
* display name
*/
name: string;
/**
* npm package name
*/
package: string;
/**
* name of the executable file
*/
binName: string;
/**
* currently installed?
*/
installed: boolean;
/**
* homepage
*/
url: string;
/**
* preprocessor
*/
preprocess: Function;
};

1812
node_modules/webpack-dev-server/types/lib/Server.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

10
node_modules/webpack-dev-server/types/lib/getPort.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
export = getPorts;
/**
* @param {number} basePort
* @param {string=} host
* @return {Promise<number>}
*/
declare function getPorts(
basePort: number,
host?: string | undefined,
): Promise<number>;

View File

@@ -0,0 +1,15 @@
export = BaseServer;
declare class BaseServer {
/**
* @param {import("../Server")} server
*/
constructor(server: import("../Server"));
/** @type {import("../Server")} */
server: import("../Server");
/** @type {ClientConnection[]} */
clients: ClientConnection[];
}
declare namespace BaseServer {
export { ClientConnection };
}
type ClientConnection = import("../Server").ClientConnection;

View File

@@ -0,0 +1,12 @@
export = SockJSServer;
declare class SockJSServer extends BaseServer {
implementation: sockjs.Server;
}
declare namespace SockJSServer {
export { WebSocketServerConfiguration, ClientConnection };
}
import BaseServer = require("./BaseServer");
import sockjs = require("sockjs");
type WebSocketServerConfiguration =
import("../Server").WebSocketServerConfiguration;
type ClientConnection = import("../Server").ClientConnection;

View File

@@ -0,0 +1,16 @@
export = WebsocketServer;
declare class WebsocketServer extends BaseServer {
static heartbeatInterval: number;
implementation: WebSocket.Server<
typeof WebSocket,
typeof import("http").IncomingMessage
>;
}
declare namespace WebsocketServer {
export { WebSocketServerConfiguration, ClientConnection };
}
import BaseServer = require("./BaseServer");
import WebSocket = require("ws");
type WebSocketServerConfiguration =
import("../Server").WebSocketServerConfiguration;
type ClientConnection = import("../Server").ClientConnection;