WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
function _temporalUndefined() {}
|
||||
export { _temporalUndefined as default };
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"characterCodes.enum.d.ts","sourceRoot":"","sources":["../../src/enums/characterCodes.enum.ts"],"names":[],"mappings":"AAAA,oBAAY,cAAc;IACtB,GAAG,KAAK;IACR,aAAa,IAAI;IACjB,iBAAiB,MAAO;IAExB,QAAQ,KAAO,CAAE,KAAK;IACtB,cAAc,KAAO,CAAE,KAAK;IAC5B,aAAa,OAAS;IACtB,kBAAkB,OAAS;IAC3B,QAAQ,MAAS;IAGjB,KAAK,KAAS,CAAE,MAAM;IACtB,gBAAgB,MAAS,CAAE,EAAE;IAC7B,MAAM,OAAS;IACf,MAAM,OAAS;IACf,OAAO,OAAS;IAChB,OAAO,OAAS;IAChB,eAAe,OAAS;IACxB,cAAc,OAAS;IACvB,aAAa,OAAS;IACtB,WAAW,OAAS;IACpB,gBAAgB,OAAS;IACzB,SAAS,OAAS;IAClB,SAAS,OAAS;IAClB,cAAc,OAAS;IACvB,kBAAkB,OAAS;IAC3B,gBAAgB,QAAS;IACzB,iBAAiB,OAAS;IAC1B,KAAK,OAAS;IAGd,oBAAoB,QAAS;IAE7B,CAAC,KAAO;IACR,CAAC,KAAO;IAER,EAAE,KAAO;IACT,EAAE,KAAO;IACT,EAAE,KAAO;IACT,EAAE,KAAO;IACT,EAAE,KAAO;IACT,EAAE,KAAO;IACT,EAAE,KAAO;IACT,EAAE,KAAO;IACT,EAAE,KAAO;IACT,EAAE,KAAO;IAET,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IACR,CAAC,MAAO;IAER,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IACR,CAAC,KAAO;IAER,SAAS,KAAO,CAAE,IAAI;IACtB,QAAQ,KAAO,CAAE,IAAI;IACrB,EAAE,KAAO,CAAE,IAAI;IACf,SAAS,KAAO,CAAE,IAAI;IACtB,QAAQ,KAAO,CAAE,IAAI;IACrB,GAAG,MAAO,CAAE,IAAI;IAChB,KAAK,KAAO,CAAE,IAAI;IAClB,UAAU,MAAO,CAAE,IAAI;IACvB,YAAY,KAAO,CAAE,IAAI;IACzB,UAAU,KAAO,CAAE,IAAI;IACvB,KAAK,KAAO,CAAE,IAAI;IAClB,KAAK,KAAO,CAAE,IAAI;IAClB,GAAG,KAAO,CAAE,IAAI;IAChB,WAAW,KAAO,CAAE,IAAI;IACxB,MAAM,KAAO,CAAE,IAAI;IACnB,WAAW,KAAO,CAAE,IAAI;IACxB,WAAW,KAAO,CAAE,IAAI;IACxB,IAAI,KAAO,CAAE,IAAI;IACjB,QAAQ,KAAO,CAAE,IAAI;IACrB,KAAK,KAAO,CAAE,IAAI;IAClB,SAAS,MAAO,CAAE,IAAI;IACtB,WAAW,KAAO,CAAE,IAAI;IACxB,SAAS,KAAO,CAAE,IAAI;IACtB,OAAO,KAAO,CAAE,IAAI;IACpB,IAAI,KAAO,CAAE,IAAI;IACjB,QAAQ,KAAO,CAAE,IAAI;IACrB,SAAS,KAAO,CAAE,IAAI;IACtB,WAAW,KAAO,CAAE,IAAI;IACxB,KAAK,KAAO,CAAE,IAAI;IAClB,KAAK,MAAO,CAAE,IAAI;IAElB,SAAS,IAAO,CAAE,KAAK;IACvB,QAAQ,KAAO,CAAE,KAAK;IACtB,aAAa,QAAS;IACtB,GAAG,IAAO,CAAE,KAAK;IACjB,WAAW,KAAO;CACrB"}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABILITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
interface ArrayBufferConstructor {
|
||||
new (): ArrayBuffer;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
# pg-types
|
||||
|
||||
This is the code that turns all the raw text from postgres into JavaScript types for [node-postgres](https://github.com/brianc/node-postgres.git)
|
||||
|
||||
## use
|
||||
|
||||
This module is consumed and exported from the root `pg` object of node-postgres. To access it, do the following:
|
||||
|
||||
```js
|
||||
var types = require('pg').types
|
||||
```
|
||||
|
||||
Generally what you'll want to do is override how a specific data-type is parsed and turned into a JavaScript type. By default the PostgreSQL backend server returns everything as strings. Every data type corresponds to a unique `OID` within the server, and these `OIDs` are sent back with the query response. So, you need to match a particluar `OID` to a function you'd like to use to take the raw text input and produce a valid JavaScript object as a result. `null` values are never parsed.
|
||||
|
||||
Let's do something I commonly like to do on projects: return 64-bit integers `(int8)` as JavaScript integers. Because JavaScript doesn't have support for 64-bit integers node-postgres cannot confidently parse `int8` data type results as numbers because if you have a _huge_ number it will overflow and the result you'd get back from node-postgres would not be the result in the datbase. That would be a __very bad thing__ so node-postgres just returns `int8` results as strings and leaves the parsing up to you. Let's say that you know you don't and wont ever have numbers greater than `int4` in your database, but you're tired of recieving results from the `COUNT(*)` function as strings (because that function returns `int8`). You would do this:
|
||||
|
||||
```js
|
||||
var types = require('pg').types
|
||||
types.setTypeParser(20, function(val) {
|
||||
return parseInt(val)
|
||||
})
|
||||
```
|
||||
|
||||
__boom__: now you get numbers instead of strings.
|
||||
|
||||
Just as another example -- not saying this is a good idea -- let's say you want to return all dates from your database as [moment](http://momentjs.com/docs/) objects. Okay, do this:
|
||||
|
||||
```js
|
||||
var types = require('pg').types
|
||||
var moment = require('moment')
|
||||
var parseFn = function(val) {
|
||||
return val === null ? null : moment(val)
|
||||
}
|
||||
types.setTypeParser(types.builtins.TIMESTAMPTZ, parseFn)
|
||||
types.setTypeParser(types.builtins.TIMESTAMP, parseFn)
|
||||
```
|
||||
_note: I've never done that with my dates, and I'm not 100% sure moment can parse all the date strings returned from postgres. It's just an example!_
|
||||
|
||||
If you're thinking "gee, this seems pretty handy, but how can I get a list of all the OIDs in the database and what they correspond to?!?!?!" worry not:
|
||||
|
||||
```bash
|
||||
$ psql -c "select typname, oid, typarray from pg_type order by oid"
|
||||
```
|
||||
|
||||
If you want to find out the OID of a specific type:
|
||||
|
||||
```bash
|
||||
$ psql -c "select typname, oid, typarray from pg_type where typname = 'daterange' order by oid"
|
||||
```
|
||||
|
||||
:smile:
|
||||
|
||||
## license
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Brian M. Carlson
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,79 @@
|
||||
import type { Path, SourceFile } from "../ast/index.ts";
|
||||
import type { SnapshotChanges } from "./proto.ts";
|
||||
/**
|
||||
* A cached source file entry, identified by content hash.
|
||||
*/
|
||||
export interface CachedSourceFile {
|
||||
/** The cached source file object */
|
||||
file: SourceFile;
|
||||
/** The content hash from the server */
|
||||
contentHash: string;
|
||||
/** The parse options key that was used to create this file */
|
||||
parseOptionsKey: string;
|
||||
/** Set of (snapshot, project) ref keys that reference this entry */
|
||||
refs: Set<string>;
|
||||
}
|
||||
/**
|
||||
* Client-side cache for source files keyed by (path, parseOptionsKey, contentHash).
|
||||
*
|
||||
* Supports multiple versions of the same file at the same path (e.g., from
|
||||
* different snapshots with different file contents). Each version is identified
|
||||
* by its content hash and parse options key.
|
||||
*
|
||||
* Entries are ref-counted by (snapshot, project) pairs. When a snapshot is
|
||||
* disposed, all refs for that snapshot across all projects are released,
|
||||
* and entries with no remaining references are evicted.
|
||||
*
|
||||
* When a new snapshot is created, unchanged cache entries from the previous
|
||||
* snapshot are retained per-project. Only files within changed or removed
|
||||
* projects are invalidated.
|
||||
*/
|
||||
export declare class SourceFileCache {
|
||||
/** Map from path to all cached versions of that file */
|
||||
private cache;
|
||||
/** Map from snapshotId to (projectId → Set of paths fetched through that project) */
|
||||
private snapshotProjectPaths;
|
||||
/**
|
||||
* Get a cached source file already retained for the given (snapshot, project) pair.
|
||||
* This does not require a content hash or parse options key — it returns the entry
|
||||
* if one exists with a matching ref. Used to skip the server request entirely when
|
||||
* retainForSnapshot has already carried over the ref.
|
||||
*
|
||||
* A given (snapshot, project) pair always parses a file the same way, so there is
|
||||
* at most one matching entry per ref.
|
||||
*/
|
||||
getRetained(path: Path, snapshotId: number, projectId: string): SourceFile | undefined;
|
||||
/**
|
||||
* Store a source file in the cache and retain it for the given (snapshot, project) pair.
|
||||
* Returns the cached file — which may be an existing entry if the hash matches.
|
||||
*/
|
||||
set(path: Path, file: SourceFile, parseOptionsKey: string, contentHash: string, snapshotId: number, projectId: string): SourceFile;
|
||||
/**
|
||||
* Retain cache entries from a previous snapshot for a new snapshot.
|
||||
* For each project in the previous snapshot:
|
||||
* - Removed projects: skip (don't retain any refs).
|
||||
* - Changed projects: retain refs for files not listed in changedFiles/deletedFiles.
|
||||
* - Unchanged projects: retain all refs.
|
||||
*/
|
||||
retainForSnapshot(newSnapshotId: number, previousSnapshotId: number, changes: SnapshotChanges | undefined): void;
|
||||
/**
|
||||
* Release all entries retained by the given snapshot across all projects.
|
||||
* Only visits paths that the snapshot actually referenced.
|
||||
* Entries with no remaining refs are evicted.
|
||||
*/
|
||||
releaseSnapshot(snapshotId: number): void;
|
||||
private trackPath;
|
||||
/**
|
||||
* Clear all entries from the cache.
|
||||
*/
|
||||
clear(): void;
|
||||
/**
|
||||
* Get the number of unique paths in the cache.
|
||||
*/
|
||||
get size(): number;
|
||||
/**
|
||||
* Check if a path is in the cache.
|
||||
*/
|
||||
has(path: Path): boolean;
|
||||
}
|
||||
//# sourceMappingURL=sourceFileCache.d.ts.map
|
||||
@@ -0,0 +1,34 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isConditionalTest = isConditionalTest;
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
function isConditionalTest(node) {
|
||||
const parent = node.parent;
|
||||
if (parent == null) {
|
||||
return false;
|
||||
}
|
||||
if (parent.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
|
||||
return isConditionalTest(parent);
|
||||
}
|
||||
if (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
|
||||
(parent.consequent === node || parent.alternate === node)) {
|
||||
return isConditionalTest(parent);
|
||||
}
|
||||
if (parent.type === utils_1.AST_NODE_TYPES.SequenceExpression &&
|
||||
parent.expressions.at(-1) === node) {
|
||||
return isConditionalTest(parent);
|
||||
}
|
||||
if (parent.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
|
||||
parent.operator === '!') {
|
||||
return isConditionalTest(parent);
|
||||
}
|
||||
if ((parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression ||
|
||||
parent.type === utils_1.AST_NODE_TYPES.DoWhileStatement ||
|
||||
parent.type === utils_1.AST_NODE_TYPES.IfStatement ||
|
||||
parent.type === utils_1.AST_NODE_TYPES.ForStatement ||
|
||||
parent.type === utils_1.AST_NODE_TYPES.WhileStatement) &&
|
||||
parent.test === node) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import * as ts from 'typescript';
|
||||
import type { ParseSettings } from '../parseSettings';
|
||||
/**
|
||||
* Clear all of the parser caches.
|
||||
* This should only be used in testing to ensure the parser is clean between tests.
|
||||
*/
|
||||
export declare function clearWatchCaches(): void;
|
||||
/**
|
||||
* Calculate project environments using options provided by consumer and paths from config
|
||||
* @param parseSettings Internal settings for parsing the file
|
||||
* @returns The programs corresponding to the supplied tsconfig paths
|
||||
*/
|
||||
export declare function getWatchProgramsForProjects(parseSettings: ParseSettings): ts.Program[];
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "tinyrainbow",
|
||||
"version": "3.1.1",
|
||||
"packageManager": "pnpm@10.34.5",
|
||||
"description": "A small library to print colourful messages.",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"browser": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tinylibs/tinyrainbow.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/tinylibs/tinyrainbow/issues"
|
||||
},
|
||||
"homepage": "https://github.com/tinylibs/tinyrainbow#readme",
|
||||
"keywords": [
|
||||
"colors",
|
||||
"tty"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// @ts-ignore TS6133
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import * as z from "zod/v3";
|
||||
|
||||
function checkErrors(a: z.ZodTypeAny, bad: any) {
|
||||
let expected: any;
|
||||
try {
|
||||
a.parse(bad);
|
||||
} catch (error) {
|
||||
expected = (error as z.ZodError).formErrors;
|
||||
}
|
||||
try {
|
||||
a.optional().parse(bad);
|
||||
} catch (error) {
|
||||
expect((error as z.ZodError).formErrors).toEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
test("Should have error messages appropriate for the underlying type", () => {
|
||||
checkErrors(z.string().min(2), 1);
|
||||
z.string().min(2).optional().parse(undefined);
|
||||
checkErrors(z.number().gte(2), 1);
|
||||
z.number().gte(2).optional().parse(undefined);
|
||||
checkErrors(z.boolean(), "");
|
||||
z.boolean().optional().parse(undefined);
|
||||
checkErrors(z.undefined(), null);
|
||||
z.undefined().optional().parse(undefined);
|
||||
checkErrors(z.null(), {});
|
||||
z.null().optional().parse(undefined);
|
||||
checkErrors(z.object({}), 1);
|
||||
z.object({}).optional().parse(undefined);
|
||||
checkErrors(z.tuple([]), 1);
|
||||
z.tuple([]).optional().parse(undefined);
|
||||
checkErrors(z.unknown(), 1);
|
||||
z.unknown().optional().parse(undefined);
|
||||
});
|
||||
|
||||
test("unwrap", () => {
|
||||
const unwrapped = z.string().optional().unwrap();
|
||||
expect(unwrapped).toBeInstanceOf(z.ZodString);
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
import * as util from "../core/util.js";
|
||||
const error = () => {
|
||||
const Sizable = {
|
||||
string: { unit: "caractères", verb: "avoir" },
|
||||
file: { unit: "octets", verb: "avoir" },
|
||||
array: { unit: "éléments", verb: "avoir" },
|
||||
set: { unit: "éléments", verb: "avoir" },
|
||||
};
|
||||
function getSizing(origin) {
|
||||
return Sizable[origin] ?? null;
|
||||
}
|
||||
const FormatDictionary = {
|
||||
regex: "entrée",
|
||||
email: "adresse courriel",
|
||||
url: "URL",
|
||||
emoji: "emoji",
|
||||
uuid: "UUID",
|
||||
uuidv4: "UUIDv4",
|
||||
uuidv6: "UUIDv6",
|
||||
nanoid: "nanoid",
|
||||
guid: "GUID",
|
||||
cuid: "cuid",
|
||||
cuid2: "cuid2",
|
||||
ulid: "ULID",
|
||||
xid: "XID",
|
||||
ksuid: "KSUID",
|
||||
datetime: "date-heure ISO",
|
||||
date: "date ISO",
|
||||
time: "heure ISO",
|
||||
duration: "durée ISO",
|
||||
ipv4: "adresse IPv4",
|
||||
ipv6: "adresse IPv6",
|
||||
cidrv4: "plage IPv4",
|
||||
cidrv6: "plage IPv6",
|
||||
base64: "chaîne encodée en base64",
|
||||
base64url: "chaîne encodée en base64url",
|
||||
json_string: "chaîne JSON",
|
||||
e164: "numéro E.164",
|
||||
jwt: "JWT",
|
||||
template_literal: "entrée",
|
||||
};
|
||||
const TypeDictionary = {
|
||||
nan: "NaN",
|
||||
};
|
||||
return (issue) => {
|
||||
switch (issue.code) {
|
||||
case "invalid_type": {
|
||||
const expected = TypeDictionary[issue.expected] ?? issue.expected;
|
||||
const receivedType = util.parsedType(issue.input);
|
||||
const received = TypeDictionary[receivedType] ?? receivedType;
|
||||
if (/^[A-Z]/.test(issue.expected)) {
|
||||
return `Entrée invalide : attendu instanceof ${issue.expected}, reçu ${received}`;
|
||||
}
|
||||
return `Entrée invalide : attendu ${expected}, reçu ${received}`;
|
||||
}
|
||||
case "invalid_value":
|
||||
if (issue.values.length === 1)
|
||||
return `Entrée invalide : attendu ${util.stringifyPrimitive(issue.values[0])}`;
|
||||
return `Option invalide : attendu l'une des valeurs suivantes ${util.joinValues(issue.values, "|")}`;
|
||||
case "too_big": {
|
||||
const adj = issue.inclusive ? "≤" : "<";
|
||||
const sizing = getSizing(issue.origin);
|
||||
if (sizing)
|
||||
return `Trop grand : attendu que ${issue.origin ?? "la valeur"} ait ${adj}${issue.maximum.toString()} ${sizing.unit}`;
|
||||
return `Trop grand : attendu que ${issue.origin ?? "la valeur"} soit ${adj}${issue.maximum.toString()}`;
|
||||
}
|
||||
case "too_small": {
|
||||
const adj = issue.inclusive ? "≥" : ">";
|
||||
const sizing = getSizing(issue.origin);
|
||||
if (sizing) {
|
||||
return `Trop petit : attendu que ${issue.origin} ait ${adj}${issue.minimum.toString()} ${sizing.unit}`;
|
||||
}
|
||||
return `Trop petit : attendu que ${issue.origin} soit ${adj}${issue.minimum.toString()}`;
|
||||
}
|
||||
case "invalid_format": {
|
||||
const _issue = issue;
|
||||
if (_issue.format === "starts_with") {
|
||||
return `Chaîne invalide : doit commencer par "${_issue.prefix}"`;
|
||||
}
|
||||
if (_issue.format === "ends_with")
|
||||
return `Chaîne invalide : doit se terminer par "${_issue.suffix}"`;
|
||||
if (_issue.format === "includes")
|
||||
return `Chaîne invalide : doit inclure "${_issue.includes}"`;
|
||||
if (_issue.format === "regex")
|
||||
return `Chaîne invalide : doit correspondre au motif ${_issue.pattern}`;
|
||||
return `${FormatDictionary[_issue.format] ?? issue.format} invalide`;
|
||||
}
|
||||
case "not_multiple_of":
|
||||
return `Nombre invalide : doit être un multiple de ${issue.divisor}`;
|
||||
case "unrecognized_keys":
|
||||
return `Clé${issue.keys.length > 1 ? "s" : ""} non reconnue${issue.keys.length > 1 ? "s" : ""} : ${util.joinValues(issue.keys, ", ")}`;
|
||||
case "invalid_key":
|
||||
return `Clé invalide dans ${issue.origin}`;
|
||||
case "invalid_union":
|
||||
return "Entrée invalide";
|
||||
case "invalid_element":
|
||||
return `Valeur invalide dans ${issue.origin}`;
|
||||
default:
|
||||
return `Entrée invalide`;
|
||||
}
|
||||
};
|
||||
};
|
||||
export default function () {
|
||||
return {
|
||||
localeError: error(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
const ANSI_BACKGROUND_OFFSET = 10;
|
||||
|
||||
const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`;
|
||||
|
||||
const wrapAnsi256 = (offset = 0) => code => `\u001B[${38 + offset};5;${code}m`;
|
||||
|
||||
const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u001B[${38 + offset};2;${red};${green};${blue}m`;
|
||||
|
||||
const styles = {
|
||||
modifier: {
|
||||
reset: [0, 0],
|
||||
// 21 isn't widely supported and 22 does the same thing
|
||||
bold: [1, 22],
|
||||
dim: [2, 22],
|
||||
italic: [3, 23],
|
||||
underline: [4, 24],
|
||||
overline: [53, 55],
|
||||
inverse: [7, 27],
|
||||
hidden: [8, 28],
|
||||
strikethrough: [9, 29],
|
||||
},
|
||||
color: {
|
||||
black: [30, 39],
|
||||
red: [31, 39],
|
||||
green: [32, 39],
|
||||
yellow: [33, 39],
|
||||
blue: [34, 39],
|
||||
magenta: [35, 39],
|
||||
cyan: [36, 39],
|
||||
white: [37, 39],
|
||||
|
||||
// Bright color
|
||||
blackBright: [90, 39],
|
||||
gray: [90, 39], // Alias of `blackBright`
|
||||
grey: [90, 39], // Alias of `blackBright`
|
||||
redBright: [91, 39],
|
||||
greenBright: [92, 39],
|
||||
yellowBright: [93, 39],
|
||||
blueBright: [94, 39],
|
||||
magentaBright: [95, 39],
|
||||
cyanBright: [96, 39],
|
||||
whiteBright: [97, 39],
|
||||
},
|
||||
bgColor: {
|
||||
bgBlack: [40, 49],
|
||||
bgRed: [41, 49],
|
||||
bgGreen: [42, 49],
|
||||
bgYellow: [43, 49],
|
||||
bgBlue: [44, 49],
|
||||
bgMagenta: [45, 49],
|
||||
bgCyan: [46, 49],
|
||||
bgWhite: [47, 49],
|
||||
|
||||
// Bright color
|
||||
bgBlackBright: [100, 49],
|
||||
bgGray: [100, 49], // Alias of `bgBlackBright`
|
||||
bgGrey: [100, 49], // Alias of `bgBlackBright`
|
||||
bgRedBright: [101, 49],
|
||||
bgGreenBright: [102, 49],
|
||||
bgYellowBright: [103, 49],
|
||||
bgBlueBright: [104, 49],
|
||||
bgMagentaBright: [105, 49],
|
||||
bgCyanBright: [106, 49],
|
||||
bgWhiteBright: [107, 49],
|
||||
},
|
||||
};
|
||||
|
||||
export const modifierNames = Object.keys(styles.modifier);
|
||||
export const foregroundColorNames = Object.keys(styles.color);
|
||||
export const backgroundColorNames = Object.keys(styles.bgColor);
|
||||
export const colorNames = [...foregroundColorNames, ...backgroundColorNames];
|
||||
|
||||
function assembleStyles() {
|
||||
const codes = new Map();
|
||||
|
||||
for (const [groupName, group] of Object.entries(styles)) {
|
||||
for (const [styleName, style] of Object.entries(group)) {
|
||||
styles[styleName] = {
|
||||
open: `\u001B[${style[0]}m`,
|
||||
close: `\u001B[${style[1]}m`,
|
||||
};
|
||||
|
||||
group[styleName] = styles[styleName];
|
||||
|
||||
codes.set(style[0], style[1]);
|
||||
}
|
||||
|
||||
Object.defineProperty(styles, groupName, {
|
||||
value: group,
|
||||
enumerable: false,
|
||||
});
|
||||
}
|
||||
|
||||
Object.defineProperty(styles, 'codes', {
|
||||
value: codes,
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
styles.color.close = '\u001B[39m';
|
||||
styles.bgColor.close = '\u001B[49m';
|
||||
|
||||
styles.color.ansi = wrapAnsi16();
|
||||
styles.color.ansi256 = wrapAnsi256();
|
||||
styles.color.ansi16m = wrapAnsi16m();
|
||||
styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
|
||||
styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
|
||||
styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
|
||||
|
||||
// From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js
|
||||
Object.defineProperties(styles, {
|
||||
rgbToAnsi256: {
|
||||
value(red, green, blue) {
|
||||
// We use the extended greyscale palette here, with the exception of
|
||||
// black and white. normal palette only has 4 greyscale shades.
|
||||
if (red === green && green === blue) {
|
||||
if (red < 8) {
|
||||
return 16;
|
||||
}
|
||||
|
||||
if (red > 248) {
|
||||
return 231;
|
||||
}
|
||||
|
||||
return Math.round(((red - 8) / 247) * 24) + 232;
|
||||
}
|
||||
|
||||
return 16
|
||||
+ (36 * Math.round(red / 255 * 5))
|
||||
+ (6 * Math.round(green / 255 * 5))
|
||||
+ Math.round(blue / 255 * 5);
|
||||
},
|
||||
enumerable: false,
|
||||
},
|
||||
hexToRgb: {
|
||||
value(hex) {
|
||||
const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
|
||||
if (!matches) {
|
||||
return [0, 0, 0];
|
||||
}
|
||||
|
||||
let [colorString] = matches;
|
||||
|
||||
if (colorString.length === 3) {
|
||||
colorString = [...colorString].map(character => character + character).join('');
|
||||
}
|
||||
|
||||
const integer = Number.parseInt(colorString, 16);
|
||||
|
||||
return [
|
||||
/* eslint-disable no-bitwise */
|
||||
(integer >> 16) & 0xFF,
|
||||
(integer >> 8) & 0xFF,
|
||||
integer & 0xFF,
|
||||
/* eslint-enable no-bitwise */
|
||||
];
|
||||
},
|
||||
enumerable: false,
|
||||
},
|
||||
hexToAnsi256: {
|
||||
value: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
|
||||
enumerable: false,
|
||||
},
|
||||
ansi256ToAnsi: {
|
||||
value(code) {
|
||||
if (code < 8) {
|
||||
return 30 + code;
|
||||
}
|
||||
|
||||
if (code < 16) {
|
||||
return 90 + (code - 8);
|
||||
}
|
||||
|
||||
let red;
|
||||
let green;
|
||||
let blue;
|
||||
|
||||
if (code >= 232) {
|
||||
red = (((code - 232) * 10) + 8) / 255;
|
||||
green = red;
|
||||
blue = red;
|
||||
} else {
|
||||
code -= 16;
|
||||
|
||||
const remainder = code % 36;
|
||||
|
||||
red = Math.floor(code / 36) / 5;
|
||||
green = Math.floor(remainder / 6) / 5;
|
||||
blue = (remainder % 6) / 5;
|
||||
}
|
||||
|
||||
const value = Math.max(red, green, blue) * 2;
|
||||
|
||||
if (value === 0) {
|
||||
return 30;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-bitwise
|
||||
let result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red));
|
||||
|
||||
if (value === 2) {
|
||||
result += 60;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
enumerable: false,
|
||||
},
|
||||
rgbToAnsi: {
|
||||
value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
|
||||
enumerable: false,
|
||||
},
|
||||
hexToAnsi: {
|
||||
value: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
|
||||
enumerable: false,
|
||||
},
|
||||
});
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
const ansiStyles = assembleStyles();
|
||||
|
||||
export default ansiStyles;
|
||||
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _v = _interopRequireDefault(require("./v35.js"));
|
||||
|
||||
var _sha = _interopRequireDefault(require("./sha1.js"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
const v5 = (0, _v.default)('v5', 0x50, _sha.default);
|
||||
var _default = v5;
|
||||
exports.default = _default;
|
||||
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
// THIS CODE WAS AUTOMATICALLY GENERATED
|
||||
// DO NOT EDIT THIS CODE BY HAND
|
||||
// RUN THE FOLLOWING COMMAND FROM THE WORKSPACE ROOT TO REGENERATE:
|
||||
// npx nx generate-lib repo
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.esnext_typedarrays = void 0;
|
||||
const base_config_1 = require("./base-config");
|
||||
exports.esnext_typedarrays = {
|
||||
libs: [],
|
||||
variables: [
|
||||
['Uint8Array', base_config_1.TYPE],
|
||||
['Uint8ArrayConstructor', base_config_1.TYPE],
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { TSESLint } from '@typescript-eslint/utils';
|
||||
import type { TypeOrValueSpecifier } from '../util';
|
||||
type Options = [
|
||||
{
|
||||
allow?: TypeOrValueSpecifier[];
|
||||
}
|
||||
];
|
||||
type MessageIds = 'addAwait' | 'noArraySpreadInObject' | 'noClassDeclarationSpreadInObject' | 'noClassInstanceSpreadInObject' | 'noFunctionSpreadInObject' | 'noIterableSpreadInObject' | 'noMapSpreadInObject' | 'noPromiseSpreadInObject' | 'noStringSpread' | 'replaceMapSpreadInObject';
|
||||
declare const _default: TSESLint.RuleModule<MessageIds, Options, import("../../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
||||
name: string;
|
||||
};
|
||||
export default _default;
|
||||
@@ -0,0 +1,424 @@
|
||||
/**
|
||||
* @fileoverview Rule to forbid or enforce dangling commas.
|
||||
* @author Ian Christian Myers
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const DEFAULT_OPTIONS = Object.freeze({
|
||||
arrays: "never",
|
||||
objects: "never",
|
||||
imports: "never",
|
||||
exports: "never",
|
||||
functions: "never",
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks whether or not a trailing comma is allowed in a given node.
|
||||
* If the `lastItem` is `RestElement` or `RestProperty`, it disallows trailing commas.
|
||||
* @param {ASTNode} lastItem The node of the last element in the given node.
|
||||
* @returns {boolean} `true` if a trailing comma is allowed.
|
||||
*/
|
||||
function isTrailingCommaAllowed(lastItem) {
|
||||
return !(
|
||||
lastItem.type === "RestElement" ||
|
||||
lastItem.type === "RestProperty" ||
|
||||
lastItem.type === "ExperimentalRestProperty"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize option value.
|
||||
* @param {string|Object|undefined} optionValue The 1st option value to normalize.
|
||||
* @param {number} ecmaVersion The normalized ECMAScript version.
|
||||
* @returns {Object} The normalized option value.
|
||||
*/
|
||||
function normalizeOptions(optionValue, ecmaVersion) {
|
||||
if (typeof optionValue === "string") {
|
||||
return {
|
||||
arrays: optionValue,
|
||||
objects: optionValue,
|
||||
imports: optionValue,
|
||||
exports: optionValue,
|
||||
functions: ecmaVersion < 2017 ? "ignore" : optionValue,
|
||||
};
|
||||
}
|
||||
if (typeof optionValue === "object" && optionValue !== null) {
|
||||
return {
|
||||
arrays: optionValue.arrays || DEFAULT_OPTIONS.arrays,
|
||||
objects: optionValue.objects || DEFAULT_OPTIONS.objects,
|
||||
imports: optionValue.imports || DEFAULT_OPTIONS.imports,
|
||||
exports: optionValue.exports || DEFAULT_OPTIONS.exports,
|
||||
functions: optionValue.functions || DEFAULT_OPTIONS.functions,
|
||||
};
|
||||
}
|
||||
|
||||
return DEFAULT_OPTIONS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
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: "11.0.0",
|
||||
replacedBy: [
|
||||
{
|
||||
message:
|
||||
"ESLint Stylistic now maintains deprecated stylistic core rules.",
|
||||
url: "https://eslint.style/guide/migration",
|
||||
plugin: {
|
||||
name: "@stylistic/eslint-plugin",
|
||||
url: "https://eslint.style",
|
||||
},
|
||||
rule: {
|
||||
name: "comma-dangle",
|
||||
url: "https://eslint.style/rules/comma-dangle",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Require or disallow trailing commas",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/comma-dangle",
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: {
|
||||
definitions: {
|
||||
value: {
|
||||
enum: [
|
||||
"always-multiline",
|
||||
"always",
|
||||
"never",
|
||||
"only-multiline",
|
||||
],
|
||||
},
|
||||
valueWithIgnore: {
|
||||
enum: [
|
||||
"always-multiline",
|
||||
"always",
|
||||
"ignore",
|
||||
"never",
|
||||
"only-multiline",
|
||||
],
|
||||
},
|
||||
},
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
$ref: "#/definitions/value",
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
arrays: {
|
||||
$ref: "#/definitions/valueWithIgnore",
|
||||
},
|
||||
objects: {
|
||||
$ref: "#/definitions/valueWithIgnore",
|
||||
},
|
||||
imports: {
|
||||
$ref: "#/definitions/valueWithIgnore",
|
||||
},
|
||||
exports: {
|
||||
$ref: "#/definitions/valueWithIgnore",
|
||||
},
|
||||
functions: {
|
||||
$ref: "#/definitions/valueWithIgnore",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
additionalItems: false,
|
||||
},
|
||||
|
||||
messages: {
|
||||
unexpected: "Unexpected trailing comma.",
|
||||
missing: "Missing trailing comma.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = normalizeOptions(
|
||||
context.options[0],
|
||||
context.languageOptions.ecmaVersion,
|
||||
);
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Gets the last item of the given node.
|
||||
* @param {ASTNode} node The node to get.
|
||||
* @returns {ASTNode|null} The last node or null.
|
||||
*/
|
||||
function getLastItem(node) {
|
||||
/**
|
||||
* Returns the last element of an array
|
||||
* @param {any[]} array The input array
|
||||
* @returns {any} The last element
|
||||
*/
|
||||
function last(array) {
|
||||
return array.at(-1);
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "ObjectExpression":
|
||||
case "ObjectPattern":
|
||||
return last(node.properties);
|
||||
case "ArrayExpression":
|
||||
case "ArrayPattern":
|
||||
return last(node.elements);
|
||||
case "ImportDeclaration":
|
||||
case "ExportNamedDeclaration":
|
||||
return last(node.specifiers);
|
||||
case "FunctionDeclaration":
|
||||
case "FunctionExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
return last(node.params);
|
||||
case "CallExpression":
|
||||
case "NewExpression":
|
||||
return last(node.arguments);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the trailing comma token of the given node.
|
||||
* If the trailing comma does not exist, this returns the token which is
|
||||
* the insertion point of the trailing comma token.
|
||||
* @param {ASTNode} node The node to get.
|
||||
* @param {ASTNode} lastItem The last item of the node.
|
||||
* @returns {Token} The trailing comma token or the insertion point.
|
||||
*/
|
||||
function getTrailingToken(node, lastItem) {
|
||||
switch (node.type) {
|
||||
case "ObjectExpression":
|
||||
case "ArrayExpression":
|
||||
case "CallExpression":
|
||||
case "NewExpression":
|
||||
return sourceCode.getLastToken(node, 1);
|
||||
default: {
|
||||
const nextToken = sourceCode.getTokenAfter(lastItem);
|
||||
|
||||
if (astUtils.isCommaToken(nextToken)) {
|
||||
return nextToken;
|
||||
}
|
||||
return sourceCode.getLastToken(lastItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is multiline.
|
||||
* This rule handles a given node as multiline when the closing parenthesis
|
||||
* and the last element are not on the same line.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is multiline.
|
||||
*/
|
||||
function isMultiline(node) {
|
||||
const lastItem = getLastItem(node);
|
||||
|
||||
if (!lastItem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const penultimateToken = getTrailingToken(node, lastItem);
|
||||
const lastToken = sourceCode.getTokenAfter(penultimateToken);
|
||||
|
||||
return lastToken.loc.end.line !== penultimateToken.loc.end.line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a trailing comma if it exists.
|
||||
* @param {ASTNode} node A node to check. Its type is one of
|
||||
* ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
|
||||
* ImportDeclaration, and ExportNamedDeclaration.
|
||||
* @returns {void}
|
||||
*/
|
||||
function forbidTrailingComma(node) {
|
||||
const lastItem = getLastItem(node);
|
||||
|
||||
if (
|
||||
!lastItem ||
|
||||
(node.type === "ImportDeclaration" &&
|
||||
lastItem.type !== "ImportSpecifier")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trailingToken = getTrailingToken(node, lastItem);
|
||||
|
||||
if (astUtils.isCommaToken(trailingToken)) {
|
||||
context.report({
|
||||
node: lastItem,
|
||||
loc: trailingToken.loc,
|
||||
messageId: "unexpected",
|
||||
*fix(fixer) {
|
||||
yield fixer.remove(trailingToken);
|
||||
|
||||
/*
|
||||
* Extend the range of the fix to include surrounding tokens to ensure
|
||||
* that the element after which the comma is removed stays _last_.
|
||||
* This intentionally makes conflicts in fix ranges with rules that may be
|
||||
* adding or removing elements in the same autofix pass.
|
||||
* https://github.com/eslint/eslint/issues/15660
|
||||
*/
|
||||
yield fixer.insertTextBefore(
|
||||
sourceCode.getTokenBefore(trailingToken),
|
||||
"",
|
||||
);
|
||||
yield fixer.insertTextAfter(
|
||||
sourceCode.getTokenAfter(trailingToken),
|
||||
"",
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the last element of a given node if it does not have a trailing
|
||||
* comma.
|
||||
*
|
||||
* If a given node is `ArrayPattern` which has `RestElement`, the trailing
|
||||
* comma is disallowed, so report if it exists.
|
||||
* @param {ASTNode} node A node to check. Its type is one of
|
||||
* ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
|
||||
* ImportDeclaration, and ExportNamedDeclaration.
|
||||
* @returns {void}
|
||||
*/
|
||||
function forceTrailingComma(node) {
|
||||
const lastItem = getLastItem(node);
|
||||
|
||||
if (
|
||||
!lastItem ||
|
||||
(node.type === "ImportDeclaration" &&
|
||||
lastItem.type !== "ImportSpecifier")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!isTrailingCommaAllowed(lastItem)) {
|
||||
forbidTrailingComma(node);
|
||||
return;
|
||||
}
|
||||
|
||||
const trailingToken = getTrailingToken(node, lastItem);
|
||||
|
||||
if (trailingToken.value !== ",") {
|
||||
context.report({
|
||||
node: lastItem,
|
||||
loc: {
|
||||
start: trailingToken.loc.end,
|
||||
end: astUtils.getNextLocation(
|
||||
sourceCode,
|
||||
trailingToken.loc.end,
|
||||
),
|
||||
},
|
||||
messageId: "missing",
|
||||
*fix(fixer) {
|
||||
yield fixer.insertTextAfter(trailingToken, ",");
|
||||
|
||||
/*
|
||||
* Extend the range of the fix to include surrounding tokens to ensure
|
||||
* that the element after which the comma is inserted stays _last_.
|
||||
* This intentionally makes conflicts in fix ranges with rules that may be
|
||||
* adding or removing elements in the same autofix pass.
|
||||
* https://github.com/eslint/eslint/issues/15660
|
||||
*/
|
||||
yield fixer.insertTextBefore(trailingToken, "");
|
||||
yield fixer.insertTextAfter(
|
||||
sourceCode.getTokenAfter(trailingToken),
|
||||
"",
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a given node is multiline, reports the last element of a given node
|
||||
* when it does not have a trailing comma.
|
||||
* Otherwise, reports a trailing comma if it exists.
|
||||
* @param {ASTNode} node A node to check. Its type is one of
|
||||
* ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
|
||||
* ImportDeclaration, and ExportNamedDeclaration.
|
||||
* @returns {void}
|
||||
*/
|
||||
function forceTrailingCommaIfMultiline(node) {
|
||||
if (isMultiline(node)) {
|
||||
forceTrailingComma(node);
|
||||
} else {
|
||||
forbidTrailingComma(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only if a given node is not multiline, reports the last element of a given node
|
||||
* when it does not have a trailing comma.
|
||||
* Otherwise, reports a trailing comma if it exists.
|
||||
* @param {ASTNode} node A node to check. Its type is one of
|
||||
* ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
|
||||
* ImportDeclaration, and ExportNamedDeclaration.
|
||||
* @returns {void}
|
||||
*/
|
||||
function allowTrailingCommaIfMultiline(node) {
|
||||
if (!isMultiline(node)) {
|
||||
forbidTrailingComma(node);
|
||||
}
|
||||
}
|
||||
|
||||
const predicate = {
|
||||
always: forceTrailingComma,
|
||||
"always-multiline": forceTrailingCommaIfMultiline,
|
||||
"only-multiline": allowTrailingCommaIfMultiline,
|
||||
never: forbidTrailingComma,
|
||||
ignore() {},
|
||||
};
|
||||
|
||||
return {
|
||||
ObjectExpression: predicate[options.objects],
|
||||
ObjectPattern: predicate[options.objects],
|
||||
|
||||
ArrayExpression: predicate[options.arrays],
|
||||
ArrayPattern: predicate[options.arrays],
|
||||
|
||||
ImportDeclaration: predicate[options.imports],
|
||||
|
||||
ExportNamedDeclaration: predicate[options.exports],
|
||||
|
||||
FunctionDeclaration: predicate[options.functions],
|
||||
FunctionExpression: predicate[options.functions],
|
||||
ArrowFunctionExpression: predicate[options.functions],
|
||||
CallExpression: predicate[options.functions],
|
||||
NewExpression: predicate[options.functions],
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { TSESLint } from '@typescript-eslint/utils';
|
||||
export type Options = [
|
||||
{
|
||||
checksConditionals?: boolean;
|
||||
checksSpreads?: boolean;
|
||||
checksVoidReturn?: boolean | ChecksVoidReturnOptions;
|
||||
}
|
||||
];
|
||||
export interface ChecksVoidReturnOptions {
|
||||
arguments?: boolean;
|
||||
attributes?: boolean;
|
||||
inheritedMethods?: boolean;
|
||||
properties?: boolean;
|
||||
returns?: boolean;
|
||||
variables?: boolean;
|
||||
}
|
||||
export type MessageId = 'conditional' | 'predicate' | 'spread' | 'voidReturnArgument' | 'voidReturnAttribute' | 'voidReturnInheritedMethod' | 'voidReturnProperty' | 'voidReturnReturnValue' | 'voidReturnVariable';
|
||||
declare const _default: TSESLint.RuleModule<MessageId, Options, import("../../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
||||
name: string;
|
||||
};
|
||||
export default _default;
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag use of unary increment and decrement operators.
|
||||
* @author Ian Christian Myers
|
||||
* @author Brody McKee (github.com/mrmckeb)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines whether the given node is the update node of a `ForStatement`.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} `true` if the node is `ForStatement` update.
|
||||
*/
|
||||
function isForStatementUpdate(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return parent.type === "ForStatement" && parent.update === node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given node is considered to be a for loop "afterthought" by the logic of this rule.
|
||||
* In particular, it returns `true` if the given node is either:
|
||||
* - The update node of a `ForStatement`: for (;; i++) {}
|
||||
* - An operand of a sequence expression that is the update node: for (;; foo(), i++) {}
|
||||
* - An operand of a sequence expression that is child of another sequence expression, etc.,
|
||||
* up to the sequence expression that is the update node: for (;; foo(), (bar(), (baz(), i++))) {}
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} `true` if the node is a for loop afterthought.
|
||||
*/
|
||||
function isForLoopAfterthought(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
if (parent.type === "SequenceExpression") {
|
||||
return isForLoopAfterthought(parent);
|
||||
}
|
||||
|
||||
return isForStatementUpdate(node);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
defaultOptions: [
|
||||
{
|
||||
allowForLoopAfterthoughts: false,
|
||||
},
|
||||
],
|
||||
|
||||
docs: {
|
||||
description: "Disallow the unary operators `++` and `--`",
|
||||
recommended: false,
|
||||
frozen: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-plusplus",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowForLoopAfterthoughts: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedUnaryOp: "Unary operator '{{operator}}' used.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const [{ allowForLoopAfterthoughts }] = context.options;
|
||||
|
||||
return {
|
||||
UpdateExpression(node) {
|
||||
if (allowForLoopAfterthoughts && isForLoopAfterthought(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedUnaryOp",
|
||||
data: {
|
||||
operator: node.operator,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { SourceMapConsumer } from '..';
|
||||
@@ -0,0 +1,21 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
interface WeakKeyTypes {
|
||||
symbol: symbol;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @fileoverview A shared list of ES3 keywords.
|
||||
* @author Josh Perez
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
module.exports = [
|
||||
"abstract",
|
||||
"boolean",
|
||||
"break",
|
||||
"byte",
|
||||
"case",
|
||||
"catch",
|
||||
"char",
|
||||
"class",
|
||||
"const",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"delete",
|
||||
"do",
|
||||
"double",
|
||||
"else",
|
||||
"enum",
|
||||
"export",
|
||||
"extends",
|
||||
"false",
|
||||
"final",
|
||||
"finally",
|
||||
"float",
|
||||
"for",
|
||||
"function",
|
||||
"goto",
|
||||
"if",
|
||||
"implements",
|
||||
"import",
|
||||
"in",
|
||||
"instanceof",
|
||||
"int",
|
||||
"interface",
|
||||
"long",
|
||||
"native",
|
||||
"new",
|
||||
"null",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"return",
|
||||
"short",
|
||||
"static",
|
||||
"super",
|
||||
"switch",
|
||||
"synchronized",
|
||||
"this",
|
||||
"throw",
|
||||
"throws",
|
||||
"transient",
|
||||
"true",
|
||||
"try",
|
||||
"typeof",
|
||||
"var",
|
||||
"void",
|
||||
"volatile",
|
||||
"while",
|
||||
"with",
|
||||
];
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { Version6Options } from './types.js';
|
||||
declare function v6(options?: Version6Options, buf?: undefined, offset?: number): string;
|
||||
declare function v6<TBuf extends Uint8Array = Uint8Array>(options: Version6Options | undefined, buf: TBuf, offset?: number): TBuf;
|
||||
export default v6;
|
||||
@@ -0,0 +1,5 @@
|
||||
import REGEX from './regex.js';
|
||||
function validate(uuid) {
|
||||
return typeof uuid === 'string' && REGEX.test(uuid);
|
||||
}
|
||||
export default validate;
|
||||
@@ -0,0 +1,658 @@
|
||||
/**
|
||||
* @fileoverview A rule to disallow unnecessary assignments`.
|
||||
* @author Yosuke Ota
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { findVariable } = require("@eslint-community/eslint-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("estree").Node} ASTNode */
|
||||
/** @typedef {import("estree").Pattern} Pattern */
|
||||
/** @typedef {import("estree").Identifier} Identifier */
|
||||
/** @typedef {import("estree").VariableDeclarator} VariableDeclarator */
|
||||
/** @typedef {import("estree").AssignmentExpression} AssignmentExpression */
|
||||
/** @typedef {import("estree").UpdateExpression} UpdateExpression */
|
||||
/** @typedef {import("estree").Expression} Expression */
|
||||
/** @typedef {import("eslint-scope").Scope} Scope */
|
||||
/** @typedef {import("eslint-scope").Variable} Variable */
|
||||
/** @typedef {import("../linter/code-path-analysis/code-path")} CodePath */
|
||||
/** @typedef {import("../linter/code-path-analysis/code-path-segment")} CodePathSegment */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Extract identifier from the given pattern node used on the left-hand side of the assignment.
|
||||
* @param {Pattern} pattern The pattern node to extract identifier
|
||||
* @returns {Iterable<Identifier>} The extracted identifier
|
||||
*/
|
||||
function* extractIdentifiersFromPattern(pattern) {
|
||||
switch (pattern.type) {
|
||||
case "Identifier":
|
||||
yield pattern;
|
||||
return;
|
||||
case "ObjectPattern":
|
||||
for (const property of pattern.properties) {
|
||||
yield* extractIdentifiersFromPattern(
|
||||
property.type === "Property" ? property.value : property,
|
||||
);
|
||||
}
|
||||
return;
|
||||
case "ArrayPattern":
|
||||
for (const element of pattern.elements) {
|
||||
if (!element) {
|
||||
continue;
|
||||
}
|
||||
yield* extractIdentifiersFromPattern(element);
|
||||
}
|
||||
return;
|
||||
case "RestElement":
|
||||
yield* extractIdentifiersFromPattern(pattern.argument);
|
||||
return;
|
||||
case "AssignmentPattern":
|
||||
yield* extractIdentifiersFromPattern(pattern.left);
|
||||
|
||||
// no default
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given identifier node is evaluated after the assignment identifier.
|
||||
* @param {AssignmentInfo} assignment The assignment info.
|
||||
* @param {Identifier} identifier The identifier to check.
|
||||
* @returns {boolean} `true` if the given identifier node is evaluated after the assignment identifier.
|
||||
*/
|
||||
function isIdentifierEvaluatedAfterAssignment(assignment, identifier) {
|
||||
if (identifier.range[0] < assignment.identifier.range[1]) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
assignment.expression &&
|
||||
assignment.expression.range[0] <= identifier.range[0] &&
|
||||
identifier.range[1] <= assignment.expression.range[1]
|
||||
) {
|
||||
/*
|
||||
* The identifier node is in an expression that is evaluated before the assignment.
|
||||
* e.g. x = id;
|
||||
* ^^ identifier to check
|
||||
* ^ assignment identifier
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* e.g.
|
||||
* x = 42; id;
|
||||
* ^^ identifier to check
|
||||
* ^ assignment identifier
|
||||
* let { x, y = id } = obj;
|
||||
* ^^ identifier to check
|
||||
* ^ assignment identifier
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given identifier node is used between the assigned identifier and the equal sign.
|
||||
*
|
||||
* e.g. let { x, y = x } = obj;
|
||||
* ^ identifier to check
|
||||
* ^ assigned identifier
|
||||
* @param {AssignmentInfo} assignment The assignment info.
|
||||
* @param {Identifier} identifier The identifier to check.
|
||||
* @returns {boolean} `true` if the given identifier node is used between the assigned identifier and the equal sign.
|
||||
*/
|
||||
function isIdentifierUsedBetweenAssignedAndEqualSign(assignment, identifier) {
|
||||
if (!assignment.expression) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
assignment.identifier.range[1] <= identifier.range[0] &&
|
||||
identifier.range[1] <= assignment.expression.range[0]
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Disallow variable assignments when the value is not used",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-useless-assignment",
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
unnecessaryAssignment:
|
||||
"The value assigned to '{{name}}' is not used in subsequent statements.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* @typedef {Object} ScopeStack
|
||||
* @property {CodePath} codePath The code path of this scope stack.
|
||||
* @property {Scope} scope The scope of this scope stack.
|
||||
* @property {ScopeStack} upper The upper scope stack.
|
||||
* @property {Record<string, ScopeStackSegmentInfo>} segments The map of ScopeStackSegmentInfo.
|
||||
* @property {Set<CodePathSegment>} currentSegments The current CodePathSegments.
|
||||
* @property {Map<Variable, AssignmentInfo[]>} assignments The map of list of AssignmentInfo for each variable.
|
||||
* @property {Array} tryStatementBlocks The array of TryStatement block nodes in this scope stack.
|
||||
*/
|
||||
/**
|
||||
* @typedef {Object} ScopeStackSegmentInfo
|
||||
* @property {CodePathSegment} segment The code path segment.
|
||||
* @property {Identifier|null} first The first identifier that appears within the segment.
|
||||
* @property {Identifier|null} last The last identifier that appears within the segment.
|
||||
* `first` and `last` are used to determine whether an identifier exists within the segment position range.
|
||||
* Since it is used as a range of segments, we should originally hold all nodes, not just identifiers,
|
||||
* but since the only nodes to be judged are identifiers, it is sufficient to have a range of identifiers.
|
||||
*/
|
||||
/**
|
||||
* @typedef {Object} AssignmentInfo
|
||||
* @property {Variable} variable The variable that is assigned.
|
||||
* @property {Identifier} identifier The identifier that is assigned.
|
||||
* @property {VariableDeclarator|AssignmentExpression|UpdateExpression} node The node where the variable was updated.
|
||||
* @property {Expression|null} expression The expression that is evaluated before the assignment.
|
||||
* @property {CodePathSegment[]} segments The code path segments where the assignment was made.
|
||||
*/
|
||||
|
||||
/** @type {ScopeStack} */
|
||||
let scopeStack = null;
|
||||
|
||||
/** @type {Set<Scope>} */
|
||||
const codePathStartScopes = new Set();
|
||||
|
||||
/**
|
||||
* Gets the scope of code path start from given scope
|
||||
* @param {Scope} scope The initial scope
|
||||
* @returns {Scope} The scope of code path start
|
||||
* @throws {Error} Unexpected error
|
||||
*/
|
||||
function getCodePathStartScope(scope) {
|
||||
let target = scope;
|
||||
|
||||
while (target) {
|
||||
if (codePathStartScopes.has(target)) {
|
||||
return target;
|
||||
}
|
||||
target = target.upper;
|
||||
}
|
||||
|
||||
// Should be unreachable
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the given scope stack.
|
||||
* @param {ScopeStack} target The scope stack to verify.
|
||||
* @returns {void}
|
||||
*/
|
||||
function verify(target) {
|
||||
/**
|
||||
* Checks whether the given identifier is used in the segment.
|
||||
* @param {CodePathSegment} segment The code path segment.
|
||||
* @param {Identifier} identifier The identifier to check.
|
||||
* @returns {boolean} `true` if the identifier is used in the segment.
|
||||
*/
|
||||
function isIdentifierUsedInSegment(segment, identifier) {
|
||||
const segmentInfo = target.segments[segment.id];
|
||||
|
||||
return (
|
||||
segmentInfo.first &&
|
||||
segmentInfo.last &&
|
||||
segmentInfo.first.range[0] <= identifier.range[0] &&
|
||||
identifier.range[1] <= segmentInfo.last.range[1]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies whether the given assignment info is an used assignment.
|
||||
* Report if it is an unused assignment.
|
||||
* @param {AssignmentInfo} targetAssignment The assignment info to verify.
|
||||
* @param {AssignmentInfo[]} allAssignments The list of all assignment info for variables.
|
||||
* @returns {void}
|
||||
*/
|
||||
function verifyAssignmentIsUsed(targetAssignment, allAssignments) {
|
||||
// Skip assignment if it is in a try block.
|
||||
const isAssignmentInTryBlock = target.tryStatementBlocks.some(
|
||||
tryBlock =>
|
||||
tryBlock.range[0] <=
|
||||
targetAssignment.identifier.range[0] &&
|
||||
targetAssignment.identifier.range[1] <=
|
||||
tryBlock.range[1],
|
||||
);
|
||||
|
||||
if (isAssignmentInTryBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SubsequentSegmentData
|
||||
* @property {CodePathSegment} segment The code path segment
|
||||
* @property {AssignmentInfo} [assignment] The first occurrence of the assignment within the segment.
|
||||
* There is no need to check if the variable is used after this assignment,
|
||||
* as the value it was assigned will be used.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Information used in `getSubsequentSegments()`.
|
||||
* To avoid unnecessary iterations, cache information that has already been iterated over,
|
||||
* and if additional iterations are needed, start iterating from the retained position.
|
||||
*/
|
||||
const subsequentSegmentData = {
|
||||
/**
|
||||
* Cache of subsequent segment information list that have already been iterated.
|
||||
* @type {SubsequentSegmentData[]}
|
||||
*/
|
||||
results: [],
|
||||
|
||||
/**
|
||||
* Subsequent segments that have already been iterated on. Used to avoid infinite loops.
|
||||
* @type {Set<CodePathSegment>}
|
||||
*/
|
||||
subsequentSegments: new Set(),
|
||||
|
||||
/**
|
||||
* Unexplored code path segment.
|
||||
* If additional iterations are needed, consume this information and iterate.
|
||||
* @type {CodePathSegment[]}
|
||||
*/
|
||||
queueSegments: targetAssignment.segments.flatMap(
|
||||
segment => segment.nextSegments,
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the subsequent segments from the segment of
|
||||
* the assignment currently being validated (targetAssignment).
|
||||
* @returns {Iterable<SubsequentSegmentData>} the subsequent segments
|
||||
*/
|
||||
function* getSubsequentSegments() {
|
||||
yield* subsequentSegmentData.results;
|
||||
|
||||
while (subsequentSegmentData.queueSegments.length > 0) {
|
||||
const nextSegment =
|
||||
subsequentSegmentData.queueSegments.shift();
|
||||
|
||||
if (
|
||||
subsequentSegmentData.subsequentSegments.has(
|
||||
nextSegment,
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
subsequentSegmentData.subsequentSegments.add(
|
||||
nextSegment,
|
||||
);
|
||||
|
||||
const assignmentInSegment = allAssignments.find(
|
||||
otherAssignment =>
|
||||
otherAssignment.segments.includes(
|
||||
nextSegment,
|
||||
) &&
|
||||
!isIdentifierUsedBetweenAssignedAndEqualSign(
|
||||
otherAssignment,
|
||||
targetAssignment.identifier,
|
||||
),
|
||||
);
|
||||
|
||||
if (!assignmentInSegment) {
|
||||
/*
|
||||
* Stores the next segment to explore.
|
||||
* If `assignmentInSegment` exists,
|
||||
* we are guarding it because we don't need to explore the next segment.
|
||||
*/
|
||||
subsequentSegmentData.queueSegments.push(
|
||||
...nextSegment.nextSegments,
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {SubsequentSegmentData} */
|
||||
const result = {
|
||||
segment: nextSegment,
|
||||
assignment: assignmentInSegment,
|
||||
};
|
||||
|
||||
subsequentSegmentData.results.push(result);
|
||||
yield result;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
targetAssignment.variable.references.some(ref => {
|
||||
const type = ref.identifier.type;
|
||||
return (
|
||||
type !== "Identifier" && type !== "JSXIdentifier"
|
||||
);
|
||||
})
|
||||
) {
|
||||
/**
|
||||
* Skip checking for a variable that has at least one non-identifier reference.
|
||||
* It's generated by plugins and cannot be handled reliably in the core rule.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
const readReferences =
|
||||
targetAssignment.variable.references.filter(reference =>
|
||||
reference.isRead(),
|
||||
);
|
||||
|
||||
if (!readReferences.length) {
|
||||
/*
|
||||
* It is not just an unnecessary assignment, but an unnecessary (unused) variable
|
||||
* and thus should not be reported by this rule because it is reported by `no-unused-vars`.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Other assignment on the current segment and after current assignment.
|
||||
*/
|
||||
const otherAssignmentAfterTargetAssignment =
|
||||
allAssignments.find(assignment => {
|
||||
if (
|
||||
assignment === targetAssignment ||
|
||||
(assignment.segments.length &&
|
||||
assignment.segments.every(
|
||||
segment =>
|
||||
!targetAssignment.segments.includes(
|
||||
segment,
|
||||
),
|
||||
))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
isIdentifierEvaluatedAfterAssignment(
|
||||
targetAssignment,
|
||||
assignment.identifier,
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
assignment.expression &&
|
||||
assignment.expression.range[0] <=
|
||||
targetAssignment.identifier.range[0] &&
|
||||
targetAssignment.identifier.range[1] <=
|
||||
assignment.expression.range[1]
|
||||
) {
|
||||
/*
|
||||
* The target assignment is in an expression that is evaluated before the assignment.
|
||||
* e.g. x=(x=1);
|
||||
* ^^^ targetAssignment
|
||||
* ^^^^^^^ assignment
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
for (const reference of readReferences) {
|
||||
/*
|
||||
* If the scope of the reference is outside the current code path scope,
|
||||
* we cannot track whether this assignment is not used.
|
||||
* For example, it can also be called asynchronously.
|
||||
*/
|
||||
if (
|
||||
target.scope !== getCodePathStartScope(reference.from)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Checks if it is used in the same segment as the target assignment.
|
||||
if (
|
||||
isIdentifierEvaluatedAfterAssignment(
|
||||
targetAssignment,
|
||||
reference.identifier,
|
||||
) &&
|
||||
(isIdentifierUsedBetweenAssignedAndEqualSign(
|
||||
targetAssignment,
|
||||
reference.identifier,
|
||||
) ||
|
||||
targetAssignment.segments.some(segment =>
|
||||
isIdentifierUsedInSegment(
|
||||
segment,
|
||||
reference.identifier,
|
||||
),
|
||||
))
|
||||
) {
|
||||
if (
|
||||
otherAssignmentAfterTargetAssignment &&
|
||||
isIdentifierEvaluatedAfterAssignment(
|
||||
otherAssignmentAfterTargetAssignment,
|
||||
reference.identifier,
|
||||
)
|
||||
) {
|
||||
// There was another assignment before the reference. Therefore, it has not been used yet.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Uses in statements after the written identifier.
|
||||
return;
|
||||
}
|
||||
|
||||
if (otherAssignmentAfterTargetAssignment) {
|
||||
/*
|
||||
* The assignment was followed by another assignment in the same segment.
|
||||
* Therefore, there is no need to check the next segment.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check subsequent segments.
|
||||
for (const subsequentSegment of getSubsequentSegments()) {
|
||||
if (
|
||||
isIdentifierUsedInSegment(
|
||||
subsequentSegment.segment,
|
||||
reference.identifier,
|
||||
)
|
||||
) {
|
||||
if (
|
||||
subsequentSegment.assignment &&
|
||||
isIdentifierEvaluatedAfterAssignment(
|
||||
subsequentSegment.assignment,
|
||||
reference.identifier,
|
||||
)
|
||||
) {
|
||||
// There was another assignment before the reference. Therefore, it has not been used yet.
|
||||
continue;
|
||||
}
|
||||
|
||||
// It is used
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
context.report({
|
||||
node: targetAssignment.identifier,
|
||||
messageId: "unnecessaryAssignment",
|
||||
data: { name: targetAssignment.identifier.name },
|
||||
});
|
||||
}
|
||||
|
||||
// Verify that each assignment in the code path is used.
|
||||
for (const assignments of target.assignments.values()) {
|
||||
assignments.sort(
|
||||
(a, b) => a.identifier.range[0] - b.identifier.range[0],
|
||||
);
|
||||
for (const assignment of assignments) {
|
||||
verifyAssignmentIsUsed(assignment, assignments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
onCodePathStart(codePath, node) {
|
||||
const scope = sourceCode.getScope(node);
|
||||
|
||||
scopeStack = {
|
||||
upper: scopeStack,
|
||||
codePath,
|
||||
scope,
|
||||
segments: Object.create(null),
|
||||
currentSegments: new Set(),
|
||||
assignments: new Map(),
|
||||
tryStatementBlocks: [],
|
||||
};
|
||||
codePathStartScopes.add(scopeStack.scope);
|
||||
},
|
||||
onCodePathEnd() {
|
||||
verify(scopeStack);
|
||||
|
||||
scopeStack = scopeStack.upper;
|
||||
},
|
||||
onCodePathSegmentStart(segment) {
|
||||
const segmentInfo = { segment, first: null, last: null };
|
||||
|
||||
scopeStack.segments[segment.id] = segmentInfo;
|
||||
scopeStack.currentSegments.add(segment);
|
||||
},
|
||||
onCodePathSegmentEnd(segment) {
|
||||
scopeStack.currentSegments.delete(segment);
|
||||
},
|
||||
TryStatement(node) {
|
||||
scopeStack.tryStatementBlocks.push(node.block);
|
||||
},
|
||||
"Identifier, JSXIdentifier"(node) {
|
||||
for (const segment of scopeStack.currentSegments) {
|
||||
const segmentInfo = scopeStack.segments[segment.id];
|
||||
|
||||
if (!segmentInfo.first) {
|
||||
segmentInfo.first = node;
|
||||
}
|
||||
segmentInfo.last = node;
|
||||
}
|
||||
},
|
||||
"VariableDeclarator[init!=null], AssignmentExpression, UpdateExpression:exit"(
|
||||
node,
|
||||
) {
|
||||
if (scopeStack.currentSegments.size === 0) {
|
||||
// Ignore unreachable segments
|
||||
return;
|
||||
}
|
||||
|
||||
const assignments = scopeStack.assignments;
|
||||
|
||||
let pattern;
|
||||
let expression = null;
|
||||
|
||||
if (node.type === "VariableDeclarator") {
|
||||
pattern = node.id;
|
||||
expression = node.init;
|
||||
} else if (node.type === "AssignmentExpression") {
|
||||
pattern = node.left;
|
||||
expression = node.right;
|
||||
} else {
|
||||
// UpdateExpression
|
||||
pattern = node.argument;
|
||||
}
|
||||
|
||||
for (const identifier of extractIdentifiersFromPattern(
|
||||
pattern,
|
||||
)) {
|
||||
const scope = sourceCode.getScope(identifier);
|
||||
|
||||
/** @type {Variable} */
|
||||
const variable = findVariable(scope, identifier);
|
||||
|
||||
if (!variable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't know where global variables are used.
|
||||
if (
|
||||
variable.scope.type === "global" &&
|
||||
variable.defs.length === 0
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the scope of the variable is outside the current code path scope,
|
||||
* we cannot track whether this assignment is not used.
|
||||
*/
|
||||
if (
|
||||
scopeStack.scope !==
|
||||
getCodePathStartScope(variable.scope)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Variables marked by `markVariableAsUsed()` or
|
||||
// exported by "exported" block comment.
|
||||
if (variable.eslintUsed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Variables exported by ESM export syntax
|
||||
if (variable.scope.type === "module") {
|
||||
if (
|
||||
variable.defs.some(
|
||||
def =>
|
||||
(def.type === "Variable" &&
|
||||
def.parent.parent.type ===
|
||||
"ExportNamedDeclaration") ||
|
||||
(def.type === "FunctionName" &&
|
||||
(def.node.parent.type ===
|
||||
"ExportNamedDeclaration" ||
|
||||
def.node.parent.type ===
|
||||
"ExportDefaultDeclaration")) ||
|
||||
(def.type === "ClassName" &&
|
||||
(def.node.parent.type ===
|
||||
"ExportNamedDeclaration" ||
|
||||
def.node.parent.type ===
|
||||
"ExportDefaultDeclaration")),
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
variable.references.some(
|
||||
reference =>
|
||||
reference.identifier.parent.type ===
|
||||
"ExportSpecifier",
|
||||
)
|
||||
) {
|
||||
// It have `export { ... }` reference.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let list = assignments.get(variable);
|
||||
|
||||
if (!list) {
|
||||
list = [];
|
||||
assignments.set(variable, list);
|
||||
}
|
||||
list.push({
|
||||
variable,
|
||||
identifier,
|
||||
node,
|
||||
expression,
|
||||
segments: [...scopeStack.currentSegments],
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,179 @@
|
||||
# safe-stable-stringify
|
||||
|
||||
Safe, deterministic and fast serialization alternative to [JSON.stringify][].
|
||||
Zero dependencies. ESM and CJS. 100% coverage.
|
||||
|
||||
Gracefully handles circular structures and bigint instead of throwing.
|
||||
|
||||
Optional custom circular values, deterministic behavior or strict JSON
|
||||
compatibility check.
|
||||
|
||||
## stringify(value[, replacer[, space]])
|
||||
|
||||
The same as [JSON.stringify][].
|
||||
|
||||
* `value` {any}
|
||||
* `replacer` {string[]|function|null}
|
||||
* `space` {number|string}
|
||||
* Returns: {string}
|
||||
|
||||
```js
|
||||
const stringify = require('safe-stable-stringify')
|
||||
|
||||
const bigint = { a: 0, c: 2n, b: 1 }
|
||||
|
||||
stringify(bigint)
|
||||
// '{"a":0,"b":1,"c":2}'
|
||||
JSON.stringify(bigint)
|
||||
// TypeError: Do not know how to serialize a BigInt
|
||||
|
||||
const circular = { b: 1, a: 0 }
|
||||
circular.circular = circular
|
||||
|
||||
stringify(circular)
|
||||
// '{"a":0,"b":1,"circular":"[Circular]"}'
|
||||
JSON.stringify(circular)
|
||||
// TypeError: Converting circular structure to JSON
|
||||
|
||||
stringify(circular, ['a', 'b'], 2)
|
||||
// {
|
||||
// "a": 0,
|
||||
// "b": 1
|
||||
// }
|
||||
```
|
||||
|
||||
## stringify.configure(options)
|
||||
|
||||
* `bigint` {boolean} If `true`, bigint values are converted to a number. Otherwise
|
||||
they are ignored. **Default:** `true`.
|
||||
* `circularValue` {string|null|undefined|ErrorConstructor} Defines the value for
|
||||
circular references. Set to `undefined`, circular properties are not
|
||||
serialized (array entries are replaced with `null`). Set to `Error`, to throw
|
||||
on circular references. **Default:** `'[Circular]'`.
|
||||
* `deterministic` {boolean|function} If `true` or a `Array#sort(comparator)`
|
||||
comparator method, guarantee a deterministic key order instead of relying on
|
||||
the insertion order. **Default:** `true`.
|
||||
* `maximumBreadth` {number} Maximum number of entries to serialize per object
|
||||
(at least one). The serialized output contains information about how many
|
||||
entries have not been serialized. Ignored properties are counted as well
|
||||
(e.g., properties with symbol values). Using the array replacer overrules this
|
||||
option. **Default:** `Infinity`
|
||||
* `maximumDepth` {number} Maximum number of object nesting levels (at least 1)
|
||||
that will be serialized. Objects at the maximum level are serialized as
|
||||
`'[Object]'` and arrays as `'[Array]'`. **Default:** `Infinity`
|
||||
* `strict` {boolean} Instead of handling any JSON value gracefully, throw an
|
||||
error in case it may not be represented as JSON (functions, NaN, ...).
|
||||
Circular values and bigint values throw as well in case either option is not
|
||||
explicitly defined. Sets and Maps are not detected as well as Symbol keys!
|
||||
**Default:** `false`
|
||||
* Returns: {function} A stringify function with the options applied.
|
||||
|
||||
```js
|
||||
import { configure } from 'safe-stable-stringify'
|
||||
|
||||
const stringify = configure({
|
||||
bigint: true,
|
||||
circularValue: 'Magic circle!',
|
||||
deterministic: false,
|
||||
maximumDepth: 1,
|
||||
maximumBreadth: 4
|
||||
})
|
||||
|
||||
const circular = {
|
||||
bigint: 999_999_999_999_999_999n,
|
||||
typed: new Uint8Array(3),
|
||||
deterministic: "I don't think so",
|
||||
}
|
||||
circular.circular = circular
|
||||
circular.ignored = true
|
||||
circular.alsoIgnored = 'Yes!'
|
||||
|
||||
const stringified = stringify(circular, null, 4)
|
||||
|
||||
console.log(stringified)
|
||||
// {
|
||||
// "bigint": 999999999999999999,
|
||||
// "typed": "[Object]",
|
||||
// "deterministic": "I don't think so",
|
||||
// "circular": "Magic circle!",
|
||||
// "...": "2 items not stringified"
|
||||
// }
|
||||
|
||||
const throwOnCircular = configure({
|
||||
circularValue: Error
|
||||
})
|
||||
|
||||
throwOnCircular(circular);
|
||||
// TypeError: Converting circular structure to JSON
|
||||
```
|
||||
|
||||
## Differences to JSON.stringify
|
||||
|
||||
1. _Circular values_ are replaced with the string `[Circular]` (configurable).
|
||||
1. _Object keys_ are sorted instead of using the insertion order (configurable).
|
||||
1. _BigInt_ values are stringified as regular number instead of throwing a
|
||||
TypeError (configurable).
|
||||
1. _Boxed primitives_ (e.g., `Number(5)`) are not unboxed and are handled as
|
||||
regular object.
|
||||
|
||||
Those are the only differences to `JSON.stringify()`. This is a side effect free
|
||||
variant and [`toJSON`][], [`replacer`][] and the [`spacer`][] work the same as
|
||||
with `JSON.stringify()`.
|
||||
|
||||
## Performance / Benchmarks
|
||||
|
||||
Currently this is by far the fastest known stable (deterministic) stringify
|
||||
implementation. This is especially important for big objects and TypedArrays.
|
||||
|
||||
(Dell Precision 5540, i7-9850H CPU @ 2.60GHz, Node.js 16.11.1)
|
||||
|
||||
```md
|
||||
simple: simple object x 3,463,894 ops/sec ±0.44% (98 runs sampled)
|
||||
simple: circular x 1,236,007 ops/sec ±0.46% (99 runs sampled)
|
||||
simple: deep x 18,942 ops/sec ±0.41% (93 runs sampled)
|
||||
simple: deep circular x 18,690 ops/sec ±0.72% (96 runs sampled)
|
||||
|
||||
replacer: simple object x 2,664,940 ops/sec ±0.31% (98 runs sampled)
|
||||
replacer: circular x 1,015,981 ops/sec ±0.09% (99 runs sampled)
|
||||
replacer: deep x 17,328 ops/sec ±0.38% (97 runs sampled)
|
||||
replacer: deep circular x 17,071 ops/sec ±0.21% (98 runs sampled)
|
||||
|
||||
array: simple object x 3,869,608 ops/sec ±0.22% (98 runs sampled)
|
||||
array: circular x 3,853,943 ops/sec ±0.45% (96 runs sampled)
|
||||
array: deep x 3,563,227 ops/sec ±0.20% (100 runs sampled)
|
||||
array: deep circular x 3,286,475 ops/sec ±0.07% (100 runs sampled)
|
||||
|
||||
indentation: simple object x 2,183,162 ops/sec ±0.66% (97 runs sampled)
|
||||
indentation: circular x 872,538 ops/sec ±0.57% (98 runs sampled)
|
||||
indentation: deep x 16,795 ops/sec ±0.48% (93 runs sampled)
|
||||
indentation: deep circular x 16,443 ops/sec ±0.40% (97 runs sampled)
|
||||
```
|
||||
|
||||
Comparing `safe-stable-stringify` with known alternatives:
|
||||
|
||||
```md
|
||||
fast-json-stable-stringify x 18,765 ops/sec ±0.71% (94 runs sampled)
|
||||
json-stable-stringify x 13,870 ops/sec ±0.72% (94 runs sampled)
|
||||
fast-stable-stringify x 21,343 ops/sec ±0.33% (95 runs sampled)
|
||||
faster-stable-stringify x 17,707 ops/sec ±0.44% (97 runs sampled)
|
||||
json-stringify-deterministic x 11,208 ops/sec ±0.57% (98 runs sampled)
|
||||
fast-safe-stringify x 21,460 ops/sec ±0.75% (99 runs sampled)
|
||||
this x 30,367 ops/sec ±0.39% (96 runs sampled)
|
||||
|
||||
The fastest is this
|
||||
```
|
||||
|
||||
The `fast-safe-stringify` comparison uses the modules stable implementation.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Sponsored by [MaibornWolff](https://www.maibornwolff.de/) and [nearForm](http://nearform.com)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
[`replacer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The%20replacer%20parameter
|
||||
[`spacer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The%20space%20argument
|
||||
[`toJSON`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior
|
||||
[JSON.stringify]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user