This commit is contained in:
2025-05-09 05:30:08 +02:00
parent 7bb10e7df4
commit 73367bad9e
5322 changed files with 1266973 additions and 313 deletions

View File

@@ -0,0 +1,47 @@
{
"name": "base64-js",
"description": "Base64 encoding/decoding in pure JS",
"version": "1.5.1",
"author": "T. Jameson Little <t.jameson.little@gmail.com>",
"typings": "index.d.ts",
"bugs": {
"url": "https://github.com/beatgammit/base64-js/issues"
},
"devDependencies": {
"babel-minify": "^0.5.1",
"benchmark": "^2.1.4",
"browserify": "^16.3.0",
"standard": "*",
"tape": "4.x"
},
"homepage": "https://github.com/beatgammit/base64-js",
"keywords": [
"base64"
],
"license": "MIT",
"main": "index.js",
"repository": {
"type": "git",
"url": "git://github.com/beatgammit/base64-js.git"
},
"scripts": {
"build": "browserify -s base64js -r ./ | minify > base64js.min.js",
"lint": "standard",
"test": "npm run lint && npm run unit",
"unit": "tape test/*.js"
},
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
}

View File

@@ -0,0 +1,69 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Ivan Kopeykin @vankop
*/
"use strict";
const PATH_QUERY_FRAGMENT_REGEXP =
/^(#?(?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
const ZERO_ESCAPE_REGEXP = /\0(.)/g;
/**
* @param {string} identifier identifier
* @returns {[string, string, string]|null} parsed identifier
*/
function parseIdentifier(identifier) {
if (!identifier) {
return null;
}
const firstEscape = identifier.indexOf("\0");
if (firstEscape < 0) {
// Fast path for inputs that don't use \0 escaping.
const queryStart = identifier.indexOf("?");
// Start at index 1 to ignore a possible leading hash.
const fragmentStart = identifier.indexOf("#", 1);
if (fragmentStart < 0) {
if (queryStart < 0) {
// No fragment, no query
return [identifier, "", ""];
}
// Query, no fragment
return [
identifier.slice(0, queryStart),
identifier.slice(queryStart),
""
];
}
if (queryStart < 0 || fragmentStart < queryStart) {
// Fragment, no query
return [
identifier.slice(0, fragmentStart),
"",
identifier.slice(fragmentStart)
];
}
// Query and fragment
return [
identifier.slice(0, queryStart),
identifier.slice(queryStart, fragmentStart),
identifier.slice(fragmentStart)
];
}
const match = PATH_QUERY_FRAGMENT_REGEXP.exec(identifier);
if (!match) return null;
return [
match[1].replace(ZERO_ESCAPE_REGEXP, "$1"),
match[2] ? match[2].replace(ZERO_ESCAPE_REGEXP, "$1") : "",
match[3] || ""
];
}
module.exports.parseIdentifier = parseIdentifier;

View File

@@ -0,0 +1,3 @@
declare function postcssPluginWarning(): void;
export { postcssPluginWarning as default };

View File

@@ -0,0 +1,125 @@
const isWindows = process.platform === 'win32' ||
process.env.OSTYPE === 'cygwin' ||
process.env.OSTYPE === 'msys'
const path = require('path')
const COLON = isWindows ? ';' : ':'
const isexe = require('isexe')
const getNotFoundError = (cmd) =>
Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' })
const getPathInfo = (cmd, opt) => {
const colon = opt.colon || COLON
// If it has a slash, then we don't bother searching the pathenv.
// just check the file itself, and that's it.
const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? ['']
: (
[
// windows always checks the cwd first
...(isWindows ? [process.cwd()] : []),
...(opt.path || process.env.PATH ||
/* istanbul ignore next: very unusual */ '').split(colon),
]
)
const pathExtExe = isWindows
? opt.pathExt || process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM'
: ''
const pathExt = isWindows ? pathExtExe.split(colon) : ['']
if (isWindows) {
if (cmd.indexOf('.') !== -1 && pathExt[0] !== '')
pathExt.unshift('')
}
return {
pathEnv,
pathExt,
pathExtExe,
}
}
const which = (cmd, opt, cb) => {
if (typeof opt === 'function') {
cb = opt
opt = {}
}
if (!opt)
opt = {}
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt)
const found = []
const step = i => new Promise((resolve, reject) => {
if (i === pathEnv.length)
return opt.all && found.length ? resolve(found)
: reject(getNotFoundError(cmd))
const ppRaw = pathEnv[i]
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw
const pCmd = path.join(pathPart, cmd)
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd
: pCmd
resolve(subStep(p, i, 0))
})
const subStep = (p, i, ii) => new Promise((resolve, reject) => {
if (ii === pathExt.length)
return resolve(step(i + 1))
const ext = pathExt[ii]
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
if (!er && is) {
if (opt.all)
found.push(p + ext)
else
return resolve(p + ext)
}
return resolve(subStep(p, i, ii + 1))
})
})
return cb ? step(0).then(res => cb(null, res), cb) : step(0)
}
const whichSync = (cmd, opt) => {
opt = opt || {}
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt)
const found = []
for (let i = 0; i < pathEnv.length; i ++) {
const ppRaw = pathEnv[i]
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw
const pCmd = path.join(pathPart, cmd)
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd
: pCmd
for (let j = 0; j < pathExt.length; j ++) {
const cur = p + pathExt[j]
try {
const is = isexe.sync(cur, { pathExt: pathExtExe })
if (is) {
if (opt.all)
found.push(cur)
else
return cur
}
} catch (ex) {}
}
}
if (opt.all && found.length)
return found
if (opt.nothrow)
return null
throw getNotFoundError(cmd)
}
module.exports = which
which.sync = whichSync

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = inheritInnerComments;
var _inherit = require("../utils/inherit.js");
function inheritInnerComments(child, parent) {
(0, _inherit.default)("innerComments", child, parent);
}
//# sourceMappingURL=inheritInnerComments.js.map

View File

@@ -0,0 +1,229 @@
'use strict';
const ansiStyles = require('ansi-styles');
const {stdout: stdoutColor, stderr: stderrColor} = require('supports-color');
const {
stringReplaceAll,
stringEncaseCRLFWithFirstIndex
} = require('./util');
const {isArray} = Array;
// `supportsColor.level` → `ansiStyles.color[name]` mapping
const levelMapping = [
'ansi',
'ansi',
'ansi256',
'ansi16m'
];
const styles = Object.create(null);
const applyOptions = (object, options = {}) => {
if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
throw new Error('The `level` option should be an integer from 0 to 3');
}
// Detect level if not set manually
const colorLevel = stdoutColor ? stdoutColor.level : 0;
object.level = options.level === undefined ? colorLevel : options.level;
};
class ChalkClass {
constructor(options) {
// eslint-disable-next-line no-constructor-return
return chalkFactory(options);
}
}
const chalkFactory = options => {
const chalk = {};
applyOptions(chalk, options);
chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_);
Object.setPrototypeOf(chalk, Chalk.prototype);
Object.setPrototypeOf(chalk.template, chalk);
chalk.template.constructor = () => {
throw new Error('`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.');
};
chalk.template.Instance = ChalkClass;
return chalk.template;
};
function Chalk(options) {
return chalkFactory(options);
}
for (const [styleName, style] of Object.entries(ansiStyles)) {
styles[styleName] = {
get() {
const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty);
Object.defineProperty(this, styleName, {value: builder});
return builder;
}
};
}
styles.visible = {
get() {
const builder = createBuilder(this, this._styler, true);
Object.defineProperty(this, 'visible', {value: builder});
return builder;
}
};
const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256'];
for (const model of usedModels) {
styles[model] = {
get() {
const {level} = this;
return function (...arguments_) {
const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler);
return createBuilder(this, styler, this._isEmpty);
};
}
};
}
for (const model of usedModels) {
const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1);
styles[bgModel] = {
get() {
const {level} = this;
return function (...arguments_) {
const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler);
return createBuilder(this, styler, this._isEmpty);
};
}
};
}
const proto = Object.defineProperties(() => {}, {
...styles,
level: {
enumerable: true,
get() {
return this._generator.level;
},
set(level) {
this._generator.level = level;
}
}
});
const createStyler = (open, close, parent) => {
let openAll;
let closeAll;
if (parent === undefined) {
openAll = open;
closeAll = close;
} else {
openAll = parent.openAll + open;
closeAll = close + parent.closeAll;
}
return {
open,
close,
openAll,
closeAll,
parent
};
};
const createBuilder = (self, _styler, _isEmpty) => {
const builder = (...arguments_) => {
if (isArray(arguments_[0]) && isArray(arguments_[0].raw)) {
// Called as a template literal, for example: chalk.red`2 + 3 = {bold ${2+3}}`
return applyStyle(builder, chalkTag(builder, ...arguments_));
}
// Single argument is hot path, implicit coercion is faster than anything
// eslint-disable-next-line no-implicit-coercion
return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' '));
};
// We alter the prototype because we must return a function, but there is
// no way to create a function with a different prototype
Object.setPrototypeOf(builder, proto);
builder._generator = self;
builder._styler = _styler;
builder._isEmpty = _isEmpty;
return builder;
};
const applyStyle = (self, string) => {
if (self.level <= 0 || !string) {
return self._isEmpty ? '' : string;
}
let styler = self._styler;
if (styler === undefined) {
return string;
}
const {openAll, closeAll} = styler;
if (string.indexOf('\u001B') !== -1) {
while (styler !== undefined) {
// Replace any instances already present with a re-opening code
// otherwise only the part of the string until said closing code
// will be colored, and the rest will simply be 'plain'.
string = stringReplaceAll(string, styler.close, styler.open);
styler = styler.parent;
}
}
// We can move both next actions out of loop, because remaining actions in loop won't have
// any/visible effect on parts we add here. Close the styling before a linebreak and reopen
// after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92
const lfIndex = string.indexOf('\n');
if (lfIndex !== -1) {
string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
}
return openAll + string + closeAll;
};
let template;
const chalkTag = (chalk, ...strings) => {
const [firstString] = strings;
if (!isArray(firstString) || !isArray(firstString.raw)) {
// If chalk() was called by itself or with a string,
// return the string itself as a string.
return strings.join(' ');
}
const arguments_ = strings.slice(1);
const parts = [firstString.raw[0]];
for (let i = 1; i < firstString.length; i++) {
parts.push(
String(arguments_[i - 1]).replace(/[{}\\]/g, '\\$&'),
String(firstString.raw[i])
);
}
if (template === undefined) {
template = require('./templates');
}
return template(chalk, parts.join(''));
};
Object.defineProperties(Chalk.prototype, styles);
const chalk = Chalk(); // eslint-disable-line new-cap
chalk.supportsColor = stdoutColor;
chalk.stderr = Chalk({level: stderrColor ? stderrColor.level : 0}); // eslint-disable-line new-cap
chalk.stderr.supportsColor = stderrColor;
module.exports = chalk;

View File

@@ -0,0 +1,29 @@
import type { Draft } from 'immer';
import type { StateCreator, StoreMutatorIdentifier } from 'zustand/vanilla';
type Immer = <T, Mps extends [StoreMutatorIdentifier, unknown][] = [], Mcs extends [StoreMutatorIdentifier, unknown][] = []>(initializer: StateCreator<T, [...Mps, ['zustand/immer', never]], Mcs>) => StateCreator<T, Mps, [['zustand/immer', never], ...Mcs]>;
declare module '../vanilla.mjs' {
interface StoreMutators<S, A> {
['zustand/immer']: WithImmer<S>;
}
}
type Write<T, U> = Omit<T, keyof U> & U;
type SkipTwo<T> = T extends {
length: 0;
} ? [] : T extends {
length: 1;
} ? [] : T extends {
length: 0 | 1;
} ? [] : T extends [unknown, unknown, ...infer A] ? A : T extends [unknown, unknown?, ...infer A] ? A : T extends [unknown?, unknown?, ...infer A] ? A : never;
type SetStateType<T extends unknown[]> = Exclude<T[0], (...args: any[]) => any>;
type WithImmer<S> = Write<S, StoreImmer<S>>;
type StoreImmer<S> = S extends {
setState: infer SetState;
} ? SetState extends {
(...a: infer A1): infer Sr1;
(...a: infer A2): infer Sr2;
} ? {
setState(nextStateOrUpdater: SetStateType<A2> | Partial<SetStateType<A2>> | ((state: Draft<SetStateType<A2>>) => void), shouldReplace?: false, ...a: SkipTwo<A1>): Sr1;
setState(nextStateOrUpdater: SetStateType<A2> | ((state: Draft<SetStateType<A2>>) => void), shouldReplace: true, ...a: SkipTwo<A2>): Sr2;
} : never : never;
export declare const immer: Immer;
export {};

View File

@@ -0,0 +1,20 @@
import SourceMap from './source-map';
import type { SourceMapInput, SourceMapLoader, Options } from './types';
export type { SourceMapSegment, EncodedSourceMap, EncodedSourceMap as RawSourceMap, DecodedSourceMap, SourceMapInput, SourceMapLoader, LoaderContext, Options, } from './types';
export type { SourceMap };
/**
* Traces through all the mappings in the root sourcemap, through the sources
* (and their sourcemaps), all the way back to the original source location.
*
* `loader` will be called every time we encounter a source file. If it returns
* a sourcemap, we will recurse into that sourcemap to continue the trace. If
* it returns a falsey value, that source file is treated as an original,
* unmodified source file.
*
* Pass `excludeContent` to exclude any self-containing source file content
* from the output sourcemap.
*
* Pass `decodedMappings` to receive a SourceMap with decoded (instead of
* VLQ encoded) mappings.
*/
export default function remapping(input: SourceMapInput | SourceMapInput[], loader: SourceMapLoader, options?: boolean | Options): SourceMap;

View File

@@ -0,0 +1,50 @@
'use strict';
const pLimit = require('p-limit');
class EndError extends Error {
constructor(value) {
super();
this.value = value;
}
}
// The input can also be a promise, so we await it
const testElement = async (element, tester) => tester(await element);
// The input can also be a promise, so we `Promise.all()` them both
const finder = async element => {
const values = await Promise.all(element);
if (values[1] === true) {
throw new EndError(values[0]);
}
return false;
};
const pLocate = async (iterable, tester, options) => {
options = {
concurrency: Infinity,
preserveOrder: true,
...options
};
const limit = pLimit(options.concurrency);
// Start all the promises concurrently with optional limit
const items = [...iterable].map(element => [element, limit(testElement, element, tester)]);
// Check the promises either serially or concurrently
const checkLimit = pLimit(options.preserveOrder ? 1 : Infinity);
try {
await Promise.all(items.map(element => checkLimit(finder, element)));
} catch (error) {
if (error instanceof EndError) {
return error.value;
}
throw error;
}
};
module.exports = pLocate;

View File

@@ -0,0 +1,995 @@
/**
* @fileoverview Helper functions for ESLint class
* @author Nicholas C. Zakas
*/
"use strict";
//-----------------------------------------------------------------------------
// Requirements
//-----------------------------------------------------------------------------
const path = require("node:path");
const fs = require("node:fs");
const fsp = fs.promises;
const isGlob = require("is-glob");
const hash = require("../cli-engine/hash");
const minimatch = require("minimatch");
const globParent = require("glob-parent");
//-----------------------------------------------------------------------------
// Fixup references
//-----------------------------------------------------------------------------
const Minimatch = minimatch.Minimatch;
const MINIMATCH_OPTIONS = { dot: true };
//-----------------------------------------------------------------------------
// Types
//-----------------------------------------------------------------------------
/**
* @typedef {Object} GlobSearch
* @property {Array<string>} patterns The normalized patterns to use for a search.
* @property {Array<string>} rawPatterns The patterns as entered by the user
* before doing any normalization.
*/
//-----------------------------------------------------------------------------
// Errors
//-----------------------------------------------------------------------------
/**
* The error type when no files match a glob.
*/
class NoFilesFoundError extends Error {
/**
* @param {string} pattern The glob pattern which was not found.
* @param {boolean} globEnabled If `false` then the pattern was a glob pattern, but glob was disabled.
*/
constructor(pattern, globEnabled) {
super(
`No files matching '${pattern}' were found${!globEnabled ? " (glob was disabled)" : ""}.`,
);
this.messageTemplate = "file-not-found";
this.messageData = { pattern, globDisabled: !globEnabled };
}
}
/**
* The error type when a search fails to match multiple patterns.
*/
class UnmatchedSearchPatternsError extends Error {
/**
* @param {Object} options The options for the error.
* @param {string} options.basePath The directory that was searched.
* @param {Array<string>} options.unmatchedPatterns The glob patterns
* which were not found.
* @param {Array<string>} options.patterns The glob patterns that were
* searched.
* @param {Array<string>} options.rawPatterns The raw glob patterns that
* were searched.
*/
constructor({ basePath, unmatchedPatterns, patterns, rawPatterns }) {
super(
`No files matching '${rawPatterns}' in '${basePath}' were found.`,
);
this.basePath = basePath;
this.unmatchedPatterns = unmatchedPatterns;
this.patterns = patterns;
this.rawPatterns = rawPatterns;
}
}
/**
* The error type when there are files matched by a glob, but all of them have been ignored.
*/
class AllFilesIgnoredError extends Error {
/**
* @param {string} pattern The glob pattern which was not found.
*/
constructor(pattern) {
super(`All files matched by '${pattern}' are ignored.`);
this.messageTemplate = "all-matched-files-ignored";
this.messageData = { pattern };
}
}
//-----------------------------------------------------------------------------
// General Helpers
//-----------------------------------------------------------------------------
/**
* Check if a given value is a non-empty string or not.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is a non-empty string.
*/
function isNonEmptyString(value) {
return typeof value === "string" && value.trim() !== "";
}
/**
* Check if a given value is an array of non-empty strings or not.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is an array of non-empty strings.
*/
function isArrayOfNonEmptyString(value) {
return (
Array.isArray(value) && value.length && value.every(isNonEmptyString)
);
}
/**
* Check if a given value is an empty array or an array of non-empty strings.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is an empty array or an array of non-empty
* strings.
*/
function isEmptyArrayOrArrayOfNonEmptyString(value) {
return Array.isArray(value) && value.every(isNonEmptyString);
}
//-----------------------------------------------------------------------------
// File-related Helpers
//-----------------------------------------------------------------------------
/**
* Normalizes slashes in a file pattern to posix-style.
* @param {string} pattern The pattern to replace slashes in.
* @returns {string} The pattern with slashes normalized.
*/
function normalizeToPosix(pattern) {
return pattern.replace(/\\/gu, "/");
}
/**
* Check if a string is a glob pattern or not.
* @param {string} pattern A glob pattern.
* @returns {boolean} `true` if the string is a glob pattern.
*/
function isGlobPattern(pattern) {
return isGlob(path.sep === "\\" ? normalizeToPosix(pattern) : pattern);
}
/**
* Determines if a given glob pattern will return any results.
* Used primarily to help with useful error messages.
* @param {Object} options The options for the function.
* @param {string} options.basePath The directory to search.
* @param {string} options.pattern An absolute path glob pattern to match.
* @returns {Promise<boolean>} True if there is a glob match, false if not.
*/
async function globMatch({ basePath, pattern }) {
let found = false;
const { hfs } = await import("@humanfs/node");
const patternToUse = normalizeToPosix(path.relative(basePath, pattern));
const matcher = new Minimatch(patternToUse, MINIMATCH_OPTIONS);
const walkSettings = {
directoryFilter(entry) {
return !found && matcher.match(entry.path, true);
},
entryFilter(entry) {
if (found || entry.isDirectory) {
return false;
}
if (matcher.match(entry.path)) {
found = true;
return true;
}
return false;
},
};
if (await hfs.isDirectory(basePath)) {
return hfs
.walk(basePath, walkSettings)
.next()
.then(() => found);
}
return found;
}
/**
* Searches a directory looking for matching glob patterns. This uses
* the config array's logic to determine if a directory or file should
* be ignored, so it is consistent with how ignoring works throughout
* ESLint.
* @param {Object} options The options for this function.
* @param {string} options.basePath The directory to search.
* @param {Array<string>} options.patterns An array of absolute path glob patterns
* to match.
* @param {Array<string>} options.rawPatterns An array of glob patterns
* as the user inputted them. Used for errors.
* @param {ConfigLoader|LegacyConfigLoader} options.configLoader The config array to use for
* determining what to ignore.
* @param {boolean} options.errorOnUnmatchedPattern Determines if an error
* should be thrown when a pattern is unmatched.
* @returns {Promise<Array<string>>} An array of matching file paths
* or an empty array if there are no matches.
* @throws {UnmatchedSearchPatternsError} If there is a pattern that doesn't
* match any files.
*/
async function globSearch({
basePath,
patterns,
rawPatterns,
configLoader,
errorOnUnmatchedPattern,
}) {
if (patterns.length === 0) {
return [];
}
/*
* In this section we are converting the patterns into Minimatch
* instances for performance reasons. Because we are doing the same
* matches repeatedly, it's best to compile those patterns once and
* reuse them multiple times.
*
* To do that, we convert any patterns with an absolute path into a
* relative path and normalize it to Posix-style slashes. We also keep
* track of the relative patterns to map them back to the original
* patterns, which we need in order to throw an error if there are any
* unmatched patterns.
*/
const relativeToPatterns = new Map();
const matchers = patterns.map((pattern, i) => {
const patternToUse = normalizeToPosix(path.relative(basePath, pattern));
relativeToPatterns.set(patternToUse, patterns[i]);
return new Minimatch(patternToUse, MINIMATCH_OPTIONS);
});
/*
* We track unmatched patterns because we may want to throw an error when
* they occur. To start, this set is initialized with all of the patterns.
* Every time a match occurs, the pattern is removed from the set, making
* it easy to tell if we have any unmatched patterns left at the end of
* search.
*/
const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
const { hfs } = await import("@humanfs/node");
const walk = hfs.walk(basePath, {
async directoryFilter(entry) {
if (!matchers.some(matcher => matcher.match(entry.path, true))) {
return false;
}
const absolutePath = path.resolve(basePath, entry.path);
const configs =
await configLoader.loadConfigArrayForDirectory(absolutePath);
return !configs.isDirectoryIgnored(absolutePath);
},
async entryFilter(entry) {
const absolutePath = path.resolve(basePath, entry.path);
// entries may be directories or files so filter out directories
if (entry.isDirectory) {
return false;
}
const configs =
await configLoader.loadConfigArrayForFile(absolutePath);
const config = configs.getConfig(absolutePath);
/*
* Optimization: We need to track when patterns are left unmatched
* and so we use `unmatchedPatterns` to do that. There is a bit of
* complexity here because the same file can be matched by more than
* one pattern. So, when we start, we actually need to test every
* pattern against every file. Once we know there are no remaining
* unmatched patterns, then we can switch to just looking for the
* first matching pattern for improved speed.
*/
const matchesPattern =
unmatchedPatterns.size > 0
? matchers.reduce((previousValue, matcher) => {
const pathMatches = matcher.match(entry.path);
/*
* We updated the unmatched patterns set only if the path
* matches and the file has a config. If the file has no
* config, that means there wasn't a match for the
* pattern so it should not be removed.
*
* Performance note: `getConfig()` aggressively caches
* results so there is no performance penalty for calling
* it multiple times with the same argument.
*/
if (pathMatches && config) {
unmatchedPatterns.delete(matcher.pattern);
}
return pathMatches || previousValue;
}, false)
: matchers.some(matcher => matcher.match(entry.path));
return matchesPattern && config !== void 0;
},
});
const filePaths = [];
if (await hfs.isDirectory(basePath)) {
for await (const entry of walk) {
filePaths.push(path.resolve(basePath, entry.path));
}
}
// now check to see if we have any unmatched patterns
if (errorOnUnmatchedPattern && unmatchedPatterns.size > 0) {
throw new UnmatchedSearchPatternsError({
basePath,
unmatchedPatterns: [...unmatchedPatterns].map(pattern =>
relativeToPatterns.get(pattern),
),
patterns,
rawPatterns,
});
}
return filePaths;
}
/**
* Throws an error for unmatched patterns. The error will only contain information about the first one.
* Checks to see if there are any ignored results for a given search.
* @param {Object} options The options for this function.
* @param {string} options.basePath The directory to search.
* @param {Array<string>} options.patterns An array of glob patterns
* that were used in the original search.
* @param {Array<string>} options.rawPatterns An array of glob patterns
* as the user inputted them. Used for errors.
* @param {Array<string>} options.unmatchedPatterns A non-empty array of absolute path glob patterns
* that were unmatched in the original search.
* @returns {void} Always throws an error.
* @throws {NoFilesFoundError} If the first unmatched pattern
* doesn't match any files even when there are no ignores.
* @throws {AllFilesIgnoredError} If the first unmatched pattern
* matches some files when there are no ignores.
*/
async function throwErrorForUnmatchedPatterns({
basePath,
patterns,
rawPatterns,
unmatchedPatterns,
}) {
const pattern = unmatchedPatterns[0];
const rawPattern = rawPatterns[patterns.indexOf(pattern)];
const patternHasMatch = await globMatch({
basePath,
pattern,
});
if (patternHasMatch) {
throw new AllFilesIgnoredError(rawPattern);
}
// if we get here there are truly no matches
throw new NoFilesFoundError(rawPattern, true);
}
/**
* Performs multiple glob searches in parallel.
* @param {Object} options The options for this function.
* @param {Map<string,GlobSearch>} options.searches
* A map of absolute path glob patterns to match.
* @param {ConfigLoader|LegacyConfigLoader} options.configLoader The config loader to use for
* determining what to ignore.
* @param {boolean} options.errorOnUnmatchedPattern Determines if an
* unmatched glob pattern should throw an error.
* @returns {Promise<Array<string>>} An array of matching file paths
* or an empty array if there are no matches.
*/
async function globMultiSearch({
searches,
configLoader,
errorOnUnmatchedPattern,
}) {
/*
* For convenience, we normalized the search map into an array of objects.
* Next, we filter out all searches that have no patterns. This happens
* primarily for the cwd, which is prepopulated in the searches map as an
* optimization. However, if it has no patterns, it means all patterns
* occur outside of the cwd and we can safely filter out that search.
*/
const normalizedSearches = [...searches]
.map(([basePath, { patterns, rawPatterns }]) => ({
basePath,
patterns,
rawPatterns,
}))
.filter(({ patterns }) => patterns.length > 0);
const results = await Promise.allSettled(
normalizedSearches.map(({ basePath, patterns, rawPatterns }) =>
globSearch({
basePath,
patterns,
rawPatterns,
configLoader,
errorOnUnmatchedPattern,
}),
),
);
/*
* The first loop handles errors from the glob searches. Since we can't
* use `await` inside `flatMap`, we process errors separately in this loop.
* This results in two iterations over `results`, but since the length is
* less than or equal to the number of globs and directories passed on the
* command line, the performance impact should be minimal.
*/
for (let i = 0; i < results.length; i++) {
const result = results[i];
const currentSearch = normalizedSearches[i];
if (result.status === "fulfilled") {
continue;
}
// if we make it here then there was an error
const error = result.reason;
// unexpected errors should be re-thrown
if (!error.basePath) {
throw error;
}
if (errorOnUnmatchedPattern) {
await throwErrorForUnmatchedPatterns({
...currentSearch,
unmatchedPatterns: error.unmatchedPatterns,
});
}
}
// second loop for `fulfulled` results
return results.flatMap(result => result.value);
}
/**
* Finds all files matching the options specified.
* @param {Object} args The arguments objects.
* @param {Array<string>} args.patterns An array of glob patterns.
* @param {boolean} args.globInputPaths true to interpret glob patterns,
* false to not interpret glob patterns.
* @param {string} args.cwd The current working directory to find from.
* @param {ConfigLoader|LegacyConfigLoader} args.configLoader The config loeader for the current run.
* @param {boolean} args.errorOnUnmatchedPattern Determines if an unmatched pattern
* should throw an error.
* @returns {Promise<Array<string>>} The fully resolved file paths.
* @throws {AllFilesIgnoredError} If there are no results due to an ignore pattern.
* @throws {NoFilesFoundError} If no files matched the given patterns.
*/
async function findFiles({
patterns,
globInputPaths,
cwd,
configLoader,
errorOnUnmatchedPattern,
}) {
const results = [];
const missingPatterns = [];
let globbyPatterns = [];
let rawPatterns = [];
const searches = new Map([
[cwd, { patterns: globbyPatterns, rawPatterns: [] }],
]);
/*
* This part is a bit involved because we need to account for
* the different ways that the patterns can match directories.
* For each different way, we need to decide if we should look
* for a config file or just use the default config. (Directories
* without a config file always use the default config.)
*
* Here are the cases:
*
* 1. A directory is passed directly (e.g., "subdir"). In this case, we
* can assume that the user intends to lint this directory and we should
* not look for a config file in the parent directory, because the only
* reason to do that would be to ignore this directory (which we already
* know we don't want to do). Instead, we use the default config until we
* get to the directory that was passed, at which point we start looking
* for config files again.
*
* 2. A dot (".") or star ("*"). In this case, we want to read
* the config file in the current directory because the user is
* explicitly asking to lint the current directory. Note that "."
* will traverse into subdirectories while "*" will not.
*
* 3. A directory is passed in the form of "subdir/subsubdir".
* In this case, we don't want to look for a config file in the
* parent directory ("subdir"). We can skip looking for a config
* file until `entry.depth` is greater than 1 because there's no
* way that the pattern can match `entry.path` yet.
*
* 4. A directory glob pattern is passed (e.g., "subd*"). We want
* this case to act like case 2 because it's unclear whether or not
* any particular directory is meant to be traversed.
*
* 5. A recursive glob pattern is passed (e.g., "**"). We want this
* case to act like case 2.
*/
// check to see if we have explicit files and directories
const filePaths = patterns.map(filePath => path.resolve(cwd, filePath));
const stats = await Promise.all(
filePaths.map(filePath => fsp.stat(filePath).catch(() => {})),
);
stats.forEach((stat, index) => {
const filePath = filePaths[index];
const pattern = normalizeToPosix(patterns[index]);
if (stat) {
// files are added directly to the list
if (stat.isFile()) {
results.push(filePath);
}
// directories need extensions attached
if (stat.isDirectory()) {
if (!searches.has(filePath)) {
searches.set(filePath, { patterns: [], rawPatterns: [] });
}
({ patterns: globbyPatterns, rawPatterns } =
searches.get(filePath));
globbyPatterns.push(`${normalizeToPosix(filePath)}/**`);
rawPatterns.push(pattern);
}
return;
}
// save patterns for later use based on whether globs are enabled
if (globInputPaths && isGlobPattern(pattern)) {
/*
* We are grouping patterns by their glob parent. This is done to
* make it easier to determine when a config file should be loaded.
*/
const basePath = path.resolve(cwd, globParent(pattern));
if (!searches.has(basePath)) {
searches.set(basePath, { patterns: [], rawPatterns: [] });
}
({ patterns: globbyPatterns, rawPatterns } =
searches.get(basePath));
globbyPatterns.push(filePath);
rawPatterns.push(pattern);
} else {
missingPatterns.push(pattern);
}
});
// there were patterns that didn't match anything, tell the user
if (errorOnUnmatchedPattern && missingPatterns.length) {
throw new NoFilesFoundError(missingPatterns[0], globInputPaths);
}
// now we are safe to do the search
const globbyResults = await globMultiSearch({
searches,
configLoader,
errorOnUnmatchedPattern,
});
return [...new Set([...results, ...globbyResults])];
}
//-----------------------------------------------------------------------------
// Results-related Helpers
//-----------------------------------------------------------------------------
/**
* Checks if the given message is an error message.
* @param {LintMessage} message The message to check.
* @returns {boolean} Whether or not the message is an error message.
* @private
*/
function isErrorMessage(message) {
return message.severity === 2;
}
/**
* Returns result with warning by ignore settings
* @param {string} filePath Absolute file path of checked code
* @param {string} baseDir Absolute path of base directory
* @param {"ignored"|"external"|"unconfigured"} configStatus A status that determines why the file is ignored
* @returns {LintResult} Result with single warning
* @private
*/
function createIgnoreResult(filePath, baseDir, configStatus) {
let message;
switch (configStatus) {
case "external":
message = "File ignored because outside of base path.";
break;
case "unconfigured":
message =
"File ignored because no matching configuration was supplied.";
break;
default:
{
const isInNodeModules =
baseDir &&
path
.dirname(path.relative(baseDir, filePath))
.split(path.sep)
.includes("node_modules");
if (isInNodeModules) {
message =
'File ignored by default because it is located under the node_modules directory. Use ignore pattern "!**/node_modules/" to disable file ignore settings or use "--no-warn-ignored" to suppress this warning.';
} else {
message =
'File ignored because of a matching ignore pattern. Use "--no-ignore" to disable file ignore settings or use "--no-warn-ignored" to suppress this warning.';
}
}
break;
}
return {
filePath,
messages: [
{
ruleId: null,
fatal: false,
severity: 1,
message,
nodeType: null,
},
],
suppressedMessages: [],
errorCount: 0,
warningCount: 1,
fatalErrorCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0,
};
}
//-----------------------------------------------------------------------------
// Options-related Helpers
//-----------------------------------------------------------------------------
/**
* Check if a given value is a valid fix type or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is valid fix type.
*/
function isFixType(x) {
return (
x === "directive" ||
x === "problem" ||
x === "suggestion" ||
x === "layout"
);
}
/**
* Check if a given value is an array of fix types or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is an array of fix types.
*/
function isFixTypeArray(x) {
return Array.isArray(x) && x.every(isFixType);
}
/**
* The error for invalid options.
*/
class ESLintInvalidOptionsError extends Error {
constructor(messages) {
super(`Invalid Options:\n- ${messages.join("\n- ")}`);
this.code = "ESLINT_INVALID_OPTIONS";
Error.captureStackTrace(this, ESLintInvalidOptionsError);
}
}
/**
* Validates and normalizes options for the wrapped CLIEngine instance.
* @param {ESLintOptions} options The options to process.
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
* @returns {ESLintOptions} The normalized options.
*/
function processOptions({
allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
baseConfig = null,
cache = false,
cacheLocation = ".eslintcache",
cacheStrategy = "metadata",
cwd = process.cwd(),
errorOnUnmatchedPattern = true,
fix = false,
fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
flags = [],
globInputPaths = true,
ignore = true,
ignorePatterns = null,
overrideConfig = null,
overrideConfigFile = null,
plugins = {},
stats = false,
warnIgnored = true,
passOnNoPatterns = false,
ruleFilter = () => true,
...unknownOptions
}) {
const errors = [];
const unknownOptionKeys = Object.keys(unknownOptions);
if (unknownOptionKeys.length >= 1) {
errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`);
if (unknownOptionKeys.includes("cacheFile")) {
errors.push(
"'cacheFile' has been removed. Please use the 'cacheLocation' option instead.",
);
}
if (unknownOptionKeys.includes("configFile")) {
errors.push(
"'configFile' has been removed. Please use the 'overrideConfigFile' option instead.",
);
}
if (unknownOptionKeys.includes("envs")) {
errors.push("'envs' has been removed.");
}
if (unknownOptionKeys.includes("extensions")) {
errors.push("'extensions' has been removed.");
}
if (unknownOptionKeys.includes("resolvePluginsRelativeTo")) {
errors.push("'resolvePluginsRelativeTo' has been removed.");
}
if (unknownOptionKeys.includes("globals")) {
errors.push(
"'globals' has been removed. Please use the 'overrideConfig.languageOptions.globals' option instead.",
);
}
if (unknownOptionKeys.includes("ignorePath")) {
errors.push("'ignorePath' has been removed.");
}
if (unknownOptionKeys.includes("ignorePattern")) {
errors.push(
"'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.",
);
}
if (unknownOptionKeys.includes("parser")) {
errors.push(
"'parser' has been removed. Please use the 'overrideConfig.languageOptions.parser' option instead.",
);
}
if (unknownOptionKeys.includes("parserOptions")) {
errors.push(
"'parserOptions' has been removed. Please use the 'overrideConfig.languageOptions.parserOptions' option instead.",
);
}
if (unknownOptionKeys.includes("rules")) {
errors.push(
"'rules' has been removed. Please use the 'overrideConfig.rules' option instead.",
);
}
if (unknownOptionKeys.includes("rulePaths")) {
errors.push(
"'rulePaths' has been removed. Please define your rules using plugins.",
);
}
if (unknownOptionKeys.includes("reportUnusedDisableDirectives")) {
errors.push(
"'reportUnusedDisableDirectives' has been removed. Please use the 'overrideConfig.linterOptions.reportUnusedDisableDirectives' option instead.",
);
}
}
if (typeof allowInlineConfig !== "boolean") {
errors.push("'allowInlineConfig' must be a boolean.");
}
if (typeof baseConfig !== "object") {
errors.push("'baseConfig' must be an object or null.");
}
if (typeof cache !== "boolean") {
errors.push("'cache' must be a boolean.");
}
if (!isNonEmptyString(cacheLocation)) {
errors.push("'cacheLocation' must be a non-empty string.");
}
if (cacheStrategy !== "metadata" && cacheStrategy !== "content") {
errors.push('\'cacheStrategy\' must be any of "metadata", "content".');
}
if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
errors.push("'cwd' must be an absolute path.");
}
if (typeof errorOnUnmatchedPattern !== "boolean") {
errors.push("'errorOnUnmatchedPattern' must be a boolean.");
}
if (typeof fix !== "boolean" && typeof fix !== "function") {
errors.push("'fix' must be a boolean or a function.");
}
if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
errors.push(
'\'fixTypes\' must be an array of any of "directive", "problem", "suggestion", and "layout".',
);
}
if (!isEmptyArrayOrArrayOfNonEmptyString(flags)) {
errors.push("'flags' must be an array of non-empty strings.");
}
if (typeof globInputPaths !== "boolean") {
errors.push("'globInputPaths' must be a boolean.");
}
if (typeof ignore !== "boolean") {
errors.push("'ignore' must be a boolean.");
}
if (
!isEmptyArrayOrArrayOfNonEmptyString(ignorePatterns) &&
ignorePatterns !== null
) {
errors.push(
"'ignorePatterns' must be an array of non-empty strings or null.",
);
}
if (typeof overrideConfig !== "object") {
errors.push("'overrideConfig' must be an object or null.");
}
if (
!isNonEmptyString(overrideConfigFile) &&
overrideConfigFile !== null &&
overrideConfigFile !== true
) {
errors.push(
"'overrideConfigFile' must be a non-empty string, null, or true.",
);
}
if (typeof passOnNoPatterns !== "boolean") {
errors.push("'passOnNoPatterns' must be a boolean.");
}
if (typeof plugins !== "object") {
errors.push("'plugins' must be an object or null.");
} else if (plugins !== null && Object.keys(plugins).includes("")) {
errors.push("'plugins' must not include an empty string.");
}
if (Array.isArray(plugins)) {
errors.push(
"'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.",
);
}
if (typeof stats !== "boolean") {
errors.push("'stats' must be a boolean.");
}
if (typeof warnIgnored !== "boolean") {
errors.push("'warnIgnored' must be a boolean.");
}
if (typeof ruleFilter !== "function") {
errors.push("'ruleFilter' must be a function.");
}
if (errors.length > 0) {
throw new ESLintInvalidOptionsError(errors);
}
return {
allowInlineConfig,
baseConfig,
cache,
cacheLocation,
cacheStrategy,
// when overrideConfigFile is true that means don't do config file lookup
configFile: overrideConfigFile === true ? false : overrideConfigFile,
overrideConfig,
cwd: path.normalize(cwd),
errorOnUnmatchedPattern,
fix,
fixTypes,
flags: [...flags],
globInputPaths,
ignore,
ignorePatterns,
stats,
passOnNoPatterns,
warnIgnored,
ruleFilter,
};
}
//-----------------------------------------------------------------------------
// Cache-related helpers
//-----------------------------------------------------------------------------
/**
* return the cacheFile to be used by eslint, based on whether the provided parameter is
* a directory or looks like a directory (ends in `path.sep`), in which case the file
* name will be the `cacheFile/.cache_hashOfCWD`
*
* if cacheFile points to a file or looks like a file then in will just use that file
* @param {string} cacheFile The name of file to be used to store the cache
* @param {string} cwd Current working directory
* @returns {string} the resolved path to the cache file
*/
function getCacheFile(cacheFile, cwd) {
/*
* make sure the path separators are normalized for the environment/os
* keeping the trailing path separator if present
*/
const normalizedCacheFile = path.normalize(cacheFile);
const resolvedCacheFile = path.resolve(cwd, normalizedCacheFile);
const looksLikeADirectory = normalizedCacheFile.slice(-1) === path.sep;
/**
* return the name for the cache file in case the provided parameter is a directory
* @returns {string} the resolved path to the cacheFile
*/
function getCacheFileForDirectory() {
return path.join(resolvedCacheFile, `.cache_${hash(cwd)}`);
}
let fileStats;
try {
fileStats = fs.lstatSync(resolvedCacheFile);
} catch {
fileStats = null;
}
/*
* in case the file exists we need to verify if the provided path
* is a directory or a file. If it is a directory we want to create a file
* inside that directory
*/
if (fileStats) {
/*
* is a directory or is a file, but the original file the user provided
* looks like a directory but `path.resolve` removed the `last path.sep`
* so we need to still treat this like a directory
*/
if (fileStats.isDirectory() || looksLikeADirectory) {
return getCacheFileForDirectory();
}
// is file so just use that file
return resolvedCacheFile;
}
/*
* here we known the file or directory doesn't exist,
* so we will try to infer if its a directory if it looks like a directory
* for the current operating system.
*/
// if the last character passed is a path separator we assume is a directory
if (looksLikeADirectory) {
return getCacheFileForDirectory();
}
return resolvedCacheFile;
}
//-----------------------------------------------------------------------------
// Exports
//-----------------------------------------------------------------------------
module.exports = {
findFiles,
isNonEmptyString,
isArrayOfNonEmptyString,
createIgnoreResult,
isErrorMessage,
processOptions,
getCacheFile,
};

View File

@@ -0,0 +1,94 @@
.Dd May 13, 2016
.Dt jsesc 1
.Sh NAME
.Nm jsesc
.Nd escape strings for use in JavaScript string literals
.Sh SYNOPSIS
.Nm
.Op Fl s | -single-quotes Ar string
.br
.Op Fl d | -double-quotes Ar string
.br
.Op Fl w | -wrap Ar string
.br
.Op Fl e | -escape-everything Ar string
.br
.Op Fl 6 | -es6 Ar string
.br
.Op Fl l | -lowercase-hex Ar string
.br
.Op Fl j | -json Ar string
.br
.Op Fl p | -object Ar string
.br
.Op Fl p | -pretty Ar string
.br
.Op Fl v | -version
.br
.Op Fl h | -help
.Sh DESCRIPTION
.Nm
escapes strings for use in JavaScript string literals while generating the shortest possible valid ASCII-only output.
.Sh OPTIONS
.Bl -ohang -offset
.It Sy "-s, --single-quotes"
Escape any occurrences of ' in the input string as \\', so that the output can be used in a JavaScript string literal wrapped in single quotes.
.It Sy "-d, --double-quotes"
Escape any occurrences of " in the input string as \\", so that the output can be used in a JavaScript string literal wrapped in double quotes.
.It Sy "-w, --wrap"
Make sure the output is a valid JavaScript string literal wrapped in quotes. The type of quotes can be specified using the
.Ar -s | --single-quotes
or
.Ar -d | --double-quotes
settings.
.It Sy "-6, --es6"
Escape any astral Unicode symbols using ECMAScript 6 Unicode code point escape sequences.
.It Sy "-e, --escape-everything"
Escape all the symbols in the output, even printable ASCII symbols.
.It Sy "-j, --json"
Make sure the output is valid JSON. Hexadecimal character escape sequences and the \\v or \\0 escape sequences will not be used. Setting this flag enables the
.Ar -d | --double-quotes
and
.Ar -w | --wrap
settings.
.It Sy "-o, --object"
Treat the input as a JavaScript object rather than a string. Accepted values are flat arrays containing only string values, and flat objects containing only string values.
.It Sy "-p, --pretty"
Pretty-print the output for objects, using whitespace to make it more readable. Setting this flag enables the
.It Sy "-l, --lowercase-hex"
Use lowercase for alphabetical hexadecimal digits in escape sequences.
.Ar -o | --object
setting.
.It Sy "-v, --version"
Print jsesc's version.
.It Sy "-h, --help"
Show the help screen.
.El
.Sh EXIT STATUS
The
.Nm jsesc
utility exits with one of the following values:
.Pp
.Bl -tag -width flag -compact
.It Li 0
.Nm
successfully escaped the given string and printed the result.
.It Li 1
.Nm
wasn't instructed to escape anything (for example, the
.Ar --help
flag was set); or, an error occurred.
.El
.Sh EXAMPLES
.Bl -ohang -offset
.It Sy "jsesc 'foo bar baz'"
Print an escaped version of the given string.
.It Sy echo\ 'foo bar baz'\ |\ jsesc
Print an escaped version of the string that gets piped in.
.El
.Sh BUGS
jsesc's bug tracker is located at <https://github.com/mathiasbynens/jsesc/issues>.
.Sh AUTHOR
Mathias Bynens <https://mathiasbynens.be/>
.Sh WWW
<https://mths.be/jsesc>

View File

@@ -0,0 +1,54 @@
# which
Like the unix `which` utility.
Finds the first instance of a specified executable in the PATH
environment variable. Does not cache the results, so `hash -r` is not
needed when the PATH changes.
## USAGE
```javascript
var which = require('which')
// async usage
which('node', function (er, resolvedPath) {
// er is returned if no "node" is found on the PATH
// if it is found, then the absolute path to the exec is returned
})
// or promise
which('node').then(resolvedPath => { ... }).catch(er => { ... not found ... })
// sync usage
// throws if not found
var resolved = which.sync('node')
// if nothrow option is used, returns null if not found
resolved = which.sync('node', {nothrow: true})
// Pass options to override the PATH and PATHEXT environment vars.
which('node', { path: someOtherPath }, function (er, resolved) {
if (er)
throw er
console.log('found at %j', resolved)
})
```
## CLI USAGE
Same as the BSD `which(1)` binary.
```
usage: which [-as] program ...
```
## OPTIONS
You may pass an options object as the second argument.
- `path`: Use instead of the `PATH` environment variable.
- `pathExt`: Use instead of the `PATHEXT` environment variable.
- `all`: Return all matches, instead of just the first one. Note that
this means the function returns an array of strings instead of a
single string.

View File

@@ -0,0 +1,6 @@
import { Parser } from "../index.js";
export declare const parsers: {
acorn: Parser;
espree: Parser;
};

View File

@@ -0,0 +1,278 @@
/**
* @fileoverview Rule to flag block statements that do not use the one true brace style
* @author Ian Christian Myers
* @deprecated in ESLint v8.53.0
*/
"use strict";
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
deprecated: {
message: "Formatting rules are being moved out of ESLint core.",
url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
deprecatedSince: "8.53.0",
availableUntil: "10.0.0",
replacedBy: [
{
message:
"ESLint Stylistic now maintains deprecated stylistic core rules.",
url: "https://eslint.style/guide/migration",
plugin: {
name: "@stylistic/eslint-plugin-js",
url: "https://eslint.style/packages/js",
},
rule: {
name: "brace-style",
url: "https://eslint.style/rules/js/brace-style",
},
},
],
},
type: "layout",
docs: {
description: "Enforce consistent brace style for blocks",
recommended: false,
url: "https://eslint.org/docs/latest/rules/brace-style",
},
schema: [
{
enum: ["1tbs", "stroustrup", "allman"],
},
{
type: "object",
properties: {
allowSingleLine: {
type: "boolean",
default: false,
},
},
additionalProperties: false,
},
],
fixable: "whitespace",
messages: {
nextLineOpen:
"Opening curly brace does not appear on the same line as controlling statement.",
sameLineOpen:
"Opening curly brace appears on the same line as controlling statement.",
blockSameLine:
"Statement inside of curly braces should be on next line.",
nextLineClose:
"Closing curly brace does not appear on the same line as the subsequent block.",
singleLineClose:
"Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.",
sameLineClose:
"Closing curly brace appears on the same line as the subsequent block.",
},
},
create(context) {
const style = context.options[0] || "1tbs",
params = context.options[1] || {},
sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Fixes a place where a newline unexpectedly appears
* @param {Token} firstToken The token before the unexpected newline
* @param {Token} secondToken The token after the unexpected newline
* @returns {Function} A fixer function to remove the newlines between the tokens
*/
function removeNewlineBetween(firstToken, secondToken) {
const textRange = [firstToken.range[1], secondToken.range[0]];
const textBetween = sourceCode.text.slice(
textRange[0],
textRange[1],
);
// Don't do a fix if there is a comment between the tokens
if (textBetween.trim()) {
return null;
}
return fixer => fixer.replaceTextRange(textRange, " ");
}
/**
* Validates a pair of curly brackets based on the user's config
* @param {Token} openingCurly The opening curly bracket
* @param {Token} closingCurly The closing curly bracket
* @returns {void}
*/
function validateCurlyPair(openingCurly, closingCurly) {
const tokenBeforeOpeningCurly =
sourceCode.getTokenBefore(openingCurly);
const tokenAfterOpeningCurly =
sourceCode.getTokenAfter(openingCurly);
const tokenBeforeClosingCurly =
sourceCode.getTokenBefore(closingCurly);
const singleLineException =
params.allowSingleLine &&
astUtils.isTokenOnSameLine(openingCurly, closingCurly);
if (
style !== "allman" &&
!astUtils.isTokenOnSameLine(
tokenBeforeOpeningCurly,
openingCurly,
)
) {
context.report({
node: openingCurly,
messageId: "nextLineOpen",
fix: removeNewlineBetween(
tokenBeforeOpeningCurly,
openingCurly,
),
});
}
if (
style === "allman" &&
astUtils.isTokenOnSameLine(
tokenBeforeOpeningCurly,
openingCurly,
) &&
!singleLineException
) {
context.report({
node: openingCurly,
messageId: "sameLineOpen",
fix: fixer => fixer.insertTextBefore(openingCurly, "\n"),
});
}
if (
astUtils.isTokenOnSameLine(
openingCurly,
tokenAfterOpeningCurly,
) &&
tokenAfterOpeningCurly !== closingCurly &&
!singleLineException
) {
context.report({
node: openingCurly,
messageId: "blockSameLine",
fix: fixer => fixer.insertTextAfter(openingCurly, "\n"),
});
}
if (
tokenBeforeClosingCurly !== openingCurly &&
!singleLineException &&
astUtils.isTokenOnSameLine(
tokenBeforeClosingCurly,
closingCurly,
)
) {
context.report({
node: closingCurly,
messageId: "singleLineClose",
fix: fixer => fixer.insertTextBefore(closingCurly, "\n"),
});
}
}
/**
* Validates the location of a token that appears before a keyword (e.g. a newline before `else`)
* @param {Token} curlyToken The closing curly token. This is assumed to precede a keyword token (such as `else` or `finally`).
* @returns {void}
*/
function validateCurlyBeforeKeyword(curlyToken) {
const keywordToken = sourceCode.getTokenAfter(curlyToken);
if (
style === "1tbs" &&
!astUtils.isTokenOnSameLine(curlyToken, keywordToken)
) {
context.report({
node: curlyToken,
messageId: "nextLineClose",
fix: removeNewlineBetween(curlyToken, keywordToken),
});
}
if (
style !== "1tbs" &&
astUtils.isTokenOnSameLine(curlyToken, keywordToken)
) {
context.report({
node: curlyToken,
messageId: "sameLineClose",
fix: fixer => fixer.insertTextAfter(curlyToken, "\n"),
});
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
BlockStatement(node) {
if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
validateCurlyPair(
sourceCode.getFirstToken(node),
sourceCode.getLastToken(node),
);
}
},
StaticBlock(node) {
validateCurlyPair(
sourceCode.getFirstToken(node, { skip: 1 }), // skip the `static` token
sourceCode.getLastToken(node),
);
},
ClassBody(node) {
validateCurlyPair(
sourceCode.getFirstToken(node),
sourceCode.getLastToken(node),
);
},
SwitchStatement(node) {
const closingCurly = sourceCode.getLastToken(node);
const openingCurly = sourceCode.getTokenBefore(
node.cases.length ? node.cases[0] : closingCurly,
);
validateCurlyPair(openingCurly, closingCurly);
},
IfStatement(node) {
if (
node.consequent.type === "BlockStatement" &&
node.alternate
) {
// Handle the keyword after the `if` block (before `else`)
validateCurlyBeforeKeyword(
sourceCode.getLastToken(node.consequent),
);
}
},
TryStatement(node) {
// Handle the keyword after the `try` block (before `catch` or `finally`)
validateCurlyBeforeKeyword(sourceCode.getLastToken(node.block));
if (node.handler && node.finalizer) {
// Handle the keyword after the `catch` block (before `finally`)
validateCurlyBeforeKeyword(
sourceCode.getLastToken(node.handler.body),
);
}
},
};
},
};