WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
export class Path {
|
||||
/**
|
||||
* Creates a new path based on the argument type. If the argument is a string,
|
||||
* it is assumed to be a file or directory path and is converted to a Path
|
||||
* instance. If the argument is a URL, it is assumed to be a file URL and is
|
||||
* converted to a Path instance. If the argument is a Path instance, it is
|
||||
* copied into a new Path instance. If the argument is an array, it is assumed
|
||||
* to be the steps of a path and is used to create a new Path instance.
|
||||
* @param {string|URL|Path|Array<string>} pathish The value to convert to a Path instance.
|
||||
* @returns {Path} A new Path instance.
|
||||
* @throws {TypeError} When pathish is not a string, URL, Path, or Array.
|
||||
* @throws {TypeError} When pathish is a string and is empty.
|
||||
*/
|
||||
static from(pathish: string | URL | Path | Array<string>): Path;
|
||||
/**
|
||||
* Creates a new Path instance from a string.
|
||||
* @param {string} fileOrDirPath The file or directory path to convert.
|
||||
* @returns {Path} A new Path instance.
|
||||
* @deprecated Use Path.from() instead.
|
||||
*/
|
||||
static fromString(fileOrDirPath: string): Path;
|
||||
/**
|
||||
* Creates a new Path instance from a URL.
|
||||
* @param {URL} url The URL to convert.
|
||||
* @returns {Path} A new Path instance.
|
||||
* @throws {TypeError} When url is not a URL instance.
|
||||
* @throws {TypeError} When url.pathname is empty.
|
||||
* @throws {TypeError} When url.protocol is not "file:".
|
||||
* @deprecated Use Path.from() instead.
|
||||
*/
|
||||
static fromURL(url: URL): Path;
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Iterable<string>} [steps] The steps to use for the path.
|
||||
* @throws {TypeError} When steps is not iterable.
|
||||
*/
|
||||
constructor(steps?: Iterable<string>);
|
||||
/**
|
||||
* Adds steps to the end of the path.
|
||||
* @param {...string} steps The steps to add to the path.
|
||||
* @returns {void}
|
||||
*/
|
||||
push(...steps: string[]): void;
|
||||
/**
|
||||
* Removes the last step from the path.
|
||||
* @returns {string} The last step in the path.
|
||||
*/
|
||||
pop(): string;
|
||||
/**
|
||||
* Returns an iterator for steps in the path.
|
||||
* @returns {IterableIterator<string>} An iterator for the steps in the path.
|
||||
*/
|
||||
steps(): IterableIterator<string>;
|
||||
/**
|
||||
* Sets the name (the last step) of the path.
|
||||
* @type {string}
|
||||
*/
|
||||
set name(value: string);
|
||||
/**
|
||||
* Retrieves the name (the last step) of the path.
|
||||
* @type {string}
|
||||
*/
|
||||
get name(): string;
|
||||
/**
|
||||
* Retrieves the size of the path.
|
||||
* @type {number}
|
||||
*/
|
||||
get size(): number;
|
||||
/**
|
||||
* Returns the path as a string.
|
||||
* @returns {string} The path as a string.
|
||||
*/
|
||||
toString(): string;
|
||||
/**
|
||||
* Returns an iterator for the steps in the path.
|
||||
* @returns {IterableIterator<string>} An iterator for the steps in the path.
|
||||
*/
|
||||
[Symbol.iterator](): IterableIterator<string>;
|
||||
#private;
|
||||
}
|
||||
export type HfsImpl = import("@humanfs/types").HfsImpl;
|
||||
export type HfsDirectoryEntry = import("@humanfs/types").HfsDirectoryEntry;
|
||||
@@ -0,0 +1,391 @@
|
||||
/**
|
||||
* @fileoverview Comma style - enforces comma styles of two types: last and first
|
||||
* @author Vignesh Anand aka vegetableman
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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-style",
|
||||
url: "https://eslint.style/rules/comma-style",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent comma style",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/comma-style",
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["first", "last"],
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptions: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedLineBeforeAndAfterComma:
|
||||
"Bad line breaking before and after ','.",
|
||||
expectedCommaFirst: "',' should be placed first.",
|
||||
expectedCommaLast: "',' should be placed last.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const style = context.options[0] || "last",
|
||||
sourceCode = context.sourceCode;
|
||||
const exceptions = {
|
||||
ArrayPattern: true,
|
||||
ArrowFunctionExpression: true,
|
||||
CallExpression: true,
|
||||
FunctionDeclaration: true,
|
||||
FunctionExpression: true,
|
||||
ImportDeclaration: true,
|
||||
ObjectPattern: true,
|
||||
NewExpression: true,
|
||||
};
|
||||
|
||||
if (
|
||||
context.options.length === 2 &&
|
||||
Object.hasOwn(context.options[1], "exceptions")
|
||||
) {
|
||||
const keys = Object.keys(context.options[1].exceptions);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
exceptions[keys[i]] = context.options[1].exceptions[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Modified text based on the style
|
||||
* @param {string} styleType Style type
|
||||
* @param {string} text Source code text
|
||||
* @returns {string} modified text
|
||||
* @private
|
||||
*/
|
||||
function getReplacedText(styleType, text) {
|
||||
switch (styleType) {
|
||||
case "between":
|
||||
return `,${text.replace(astUtils.LINEBREAK_MATCHER, "")}`;
|
||||
|
||||
case "first":
|
||||
return `${text},`;
|
||||
|
||||
case "last":
|
||||
return `,${text}`;
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the fixer function for a given style.
|
||||
* @param {string} styleType comma style
|
||||
* @param {ASTNode} previousItemToken The token to check.
|
||||
* @param {ASTNode} commaToken The token to check.
|
||||
* @param {ASTNode} currentItemToken The token to check.
|
||||
* @returns {Function} Fixer function
|
||||
* @private
|
||||
*/
|
||||
function getFixerFunction(
|
||||
styleType,
|
||||
previousItemToken,
|
||||
commaToken,
|
||||
currentItemToken,
|
||||
) {
|
||||
const text =
|
||||
sourceCode.text.slice(
|
||||
previousItemToken.range[1],
|
||||
commaToken.range[0],
|
||||
) +
|
||||
sourceCode.text.slice(
|
||||
commaToken.range[1],
|
||||
currentItemToken.range[0],
|
||||
);
|
||||
const range = [
|
||||
previousItemToken.range[1],
|
||||
currentItemToken.range[0],
|
||||
];
|
||||
|
||||
return function (fixer) {
|
||||
return fixer.replaceTextRange(
|
||||
range,
|
||||
getReplacedText(styleType, text),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the spacing around single items in lists.
|
||||
* @param {Token} previousItemToken The last token from the previous item.
|
||||
* @param {Token} commaToken The token representing the comma.
|
||||
* @param {Token} currentItemToken The first token of the current item.
|
||||
* @param {Token} reportItem The item to use when reporting an error.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function validateCommaItemSpacing(
|
||||
previousItemToken,
|
||||
commaToken,
|
||||
currentItemToken,
|
||||
reportItem,
|
||||
) {
|
||||
// if single line
|
||||
if (
|
||||
astUtils.isTokenOnSameLine(commaToken, currentItemToken) &&
|
||||
astUtils.isTokenOnSameLine(previousItemToken, commaToken)
|
||||
) {
|
||||
// do nothing.
|
||||
} else if (
|
||||
!astUtils.isTokenOnSameLine(commaToken, currentItemToken) &&
|
||||
!astUtils.isTokenOnSameLine(previousItemToken, commaToken)
|
||||
) {
|
||||
const comment = sourceCode.getCommentsAfter(commaToken)[0];
|
||||
const styleType =
|
||||
comment &&
|
||||
comment.type === "Block" &&
|
||||
astUtils.isTokenOnSameLine(commaToken, comment)
|
||||
? style
|
||||
: "between";
|
||||
|
||||
// lone comma
|
||||
context.report({
|
||||
node: reportItem,
|
||||
loc: commaToken.loc,
|
||||
messageId: "unexpectedLineBeforeAndAfterComma",
|
||||
fix: getFixerFunction(
|
||||
styleType,
|
||||
previousItemToken,
|
||||
commaToken,
|
||||
currentItemToken,
|
||||
),
|
||||
});
|
||||
} else if (
|
||||
style === "first" &&
|
||||
!astUtils.isTokenOnSameLine(commaToken, currentItemToken)
|
||||
) {
|
||||
context.report({
|
||||
node: reportItem,
|
||||
loc: commaToken.loc,
|
||||
messageId: "expectedCommaFirst",
|
||||
fix: getFixerFunction(
|
||||
style,
|
||||
previousItemToken,
|
||||
commaToken,
|
||||
currentItemToken,
|
||||
),
|
||||
});
|
||||
} else if (
|
||||
style === "last" &&
|
||||
astUtils.isTokenOnSameLine(commaToken, currentItemToken)
|
||||
) {
|
||||
context.report({
|
||||
node: reportItem,
|
||||
loc: commaToken.loc,
|
||||
messageId: "expectedCommaLast",
|
||||
fix: getFixerFunction(
|
||||
style,
|
||||
previousItemToken,
|
||||
commaToken,
|
||||
currentItemToken,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the comma placement with regards to a declaration/property/element
|
||||
* @param {ASTNode} node The binary expression node to check
|
||||
* @param {string} property The property of the node containing child nodes.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function validateComma(node, property) {
|
||||
const items = node[property],
|
||||
arrayLiteral =
|
||||
node.type === "ArrayExpression" ||
|
||||
node.type === "ArrayPattern";
|
||||
|
||||
if (items.length > 1 || arrayLiteral) {
|
||||
// seed as opening [
|
||||
let previousItemToken = sourceCode.getFirstToken(node);
|
||||
|
||||
items.forEach(item => {
|
||||
const commaToken = item
|
||||
? sourceCode.getTokenBefore(item)
|
||||
: previousItemToken,
|
||||
currentItemToken = item
|
||||
? sourceCode.getFirstToken(item)
|
||||
: sourceCode.getTokenAfter(commaToken),
|
||||
reportItem = item || currentItemToken;
|
||||
|
||||
/*
|
||||
* This works by comparing three token locations:
|
||||
* - previousItemToken is the last token of the previous item
|
||||
* - commaToken is the location of the comma before the current item
|
||||
* - currentItemToken is the first token of the current item
|
||||
*
|
||||
* These values get switched around if item is undefined.
|
||||
* previousItemToken will refer to the last token not belonging
|
||||
* to the current item, which could be a comma or an opening
|
||||
* square bracket. currentItemToken could be a comma.
|
||||
*
|
||||
* All comparisons are done based on these tokens directly, so
|
||||
* they are always valid regardless of an undefined item.
|
||||
*/
|
||||
if (astUtils.isCommaToken(commaToken)) {
|
||||
validateCommaItemSpacing(
|
||||
previousItemToken,
|
||||
commaToken,
|
||||
currentItemToken,
|
||||
reportItem,
|
||||
);
|
||||
}
|
||||
|
||||
if (item) {
|
||||
const tokenAfterItem = sourceCode.getTokenAfter(
|
||||
item,
|
||||
astUtils.isNotClosingParenToken,
|
||||
);
|
||||
|
||||
previousItemToken = tokenAfterItem
|
||||
? sourceCode.getTokenBefore(tokenAfterItem)
|
||||
: sourceCode.ast.tokens.at(-1);
|
||||
} else {
|
||||
previousItemToken = currentItemToken;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Special case for array literals that have empty last items, such
|
||||
* as [ 1, 2, ]. These arrays only have two items show up in the
|
||||
* AST, so we need to look at the token to verify that there's no
|
||||
* dangling comma.
|
||||
*/
|
||||
if (arrayLiteral) {
|
||||
const lastToken = sourceCode.getLastToken(node),
|
||||
nextToLastToken = sourceCode.getTokenBefore(lastToken);
|
||||
|
||||
if (astUtils.isCommaToken(nextToLastToken)) {
|
||||
validateCommaItemSpacing(
|
||||
sourceCode.getTokenBefore(nextToLastToken),
|
||||
nextToLastToken,
|
||||
lastToken,
|
||||
lastToken,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
const nodes = {};
|
||||
|
||||
if (!exceptions.VariableDeclaration) {
|
||||
nodes.VariableDeclaration = function (node) {
|
||||
validateComma(node, "declarations");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ObjectExpression) {
|
||||
nodes.ObjectExpression = function (node) {
|
||||
validateComma(node, "properties");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ObjectPattern) {
|
||||
nodes.ObjectPattern = function (node) {
|
||||
validateComma(node, "properties");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ArrayExpression) {
|
||||
nodes.ArrayExpression = function (node) {
|
||||
validateComma(node, "elements");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ArrayPattern) {
|
||||
nodes.ArrayPattern = function (node) {
|
||||
validateComma(node, "elements");
|
||||
};
|
||||
}
|
||||
if (!exceptions.FunctionDeclaration) {
|
||||
nodes.FunctionDeclaration = function (node) {
|
||||
validateComma(node, "params");
|
||||
};
|
||||
}
|
||||
if (!exceptions.FunctionExpression) {
|
||||
nodes.FunctionExpression = function (node) {
|
||||
validateComma(node, "params");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ArrowFunctionExpression) {
|
||||
nodes.ArrowFunctionExpression = function (node) {
|
||||
validateComma(node, "params");
|
||||
};
|
||||
}
|
||||
if (!exceptions.CallExpression) {
|
||||
nodes.CallExpression = function (node) {
|
||||
validateComma(node, "arguments");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ImportDeclaration) {
|
||||
nodes.ImportDeclaration = function (node) {
|
||||
validateComma(node, "specifiers");
|
||||
};
|
||||
}
|
||||
if (!exceptions.NewExpression) {
|
||||
nodes.NewExpression = function (node) {
|
||||
validateComma(node, "arguments");
|
||||
};
|
||||
}
|
||||
|
||||
return nodes;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
/*! *****************************************************************************
|
||||
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.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference lib="es2022" />
|
||||
/// <reference lib="es2023.array" />
|
||||
/// <reference lib="es2023.collection" />
|
||||
/// <reference lib="es2023.intl" />
|
||||
@@ -0,0 +1,231 @@
|
||||
import { type IField } from './modular.ts';
|
||||
export type AffinePoint<T> = {
|
||||
x: T;
|
||||
y: T;
|
||||
} & {
|
||||
Z?: never;
|
||||
};
|
||||
export interface Group<T extends Group<T>> {
|
||||
double(): T;
|
||||
negate(): T;
|
||||
add(other: T): T;
|
||||
subtract(other: T): T;
|
||||
equals(other: T): boolean;
|
||||
multiply(scalar: bigint): T;
|
||||
toAffine?(invertedZ?: any): AffinePoint<any>;
|
||||
}
|
||||
/** Base interface for all elliptic curve Points. */
|
||||
export interface CurvePoint<F, P extends CurvePoint<F, P>> extends Group<P> {
|
||||
/** Affine x coordinate. Different from projective / extended X coordinate. */
|
||||
x: F;
|
||||
/** Affine y coordinate. Different from projective / extended Y coordinate. */
|
||||
y: F;
|
||||
Z?: F;
|
||||
double(): P;
|
||||
negate(): P;
|
||||
add(other: P): P;
|
||||
subtract(other: P): P;
|
||||
equals(other: P): boolean;
|
||||
multiply(scalar: bigint): P;
|
||||
assertValidity(): void;
|
||||
clearCofactor(): P;
|
||||
is0(): boolean;
|
||||
isTorsionFree(): boolean;
|
||||
isSmallOrder(): boolean;
|
||||
multiplyUnsafe(scalar: bigint): P;
|
||||
/**
|
||||
* Massively speeds up `p.multiply(n)` by using precompute tables (caching). See {@link wNAF}.
|
||||
* @param isLazy calculate cache now. Default (true) ensures it's deferred to first `multiply()`
|
||||
*/
|
||||
precompute(windowSize?: number, isLazy?: boolean): P;
|
||||
/** Converts point to 2D xy affine coordinates */
|
||||
toAffine(invertedZ?: F): AffinePoint<F>;
|
||||
toBytes(): Uint8Array;
|
||||
toHex(): string;
|
||||
}
|
||||
/** Base interface for all elliptic curve Point constructors. */
|
||||
export interface CurvePointCons<P extends CurvePoint<any, P>> {
|
||||
[Symbol.hasInstance]: (item: unknown) => boolean;
|
||||
BASE: P;
|
||||
ZERO: P;
|
||||
/** Field for basic curve math */
|
||||
Fp: IField<P_F<P>>;
|
||||
/** Scalar field, for scalars in multiply and others */
|
||||
Fn: IField<bigint>;
|
||||
/** Creates point from x, y. Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
||||
fromAffine(p: AffinePoint<P_F<P>>): P;
|
||||
fromBytes(bytes: Uint8Array): P;
|
||||
fromHex(hex: Uint8Array | string): P;
|
||||
}
|
||||
/** Returns Fp type from Point (P_F<P> == P.F) */
|
||||
export type P_F<P extends CurvePoint<any, P>> = P extends CurvePoint<infer F, P> ? F : never;
|
||||
/** Returns Fp type from PointCons (PC_F<PC> == PC.P.F) */
|
||||
export type PC_F<PC extends CurvePointCons<CurvePoint<any, any>>> = PC['Fp']['ZERO'];
|
||||
/** Returns Point type from PointCons (PC_P<PC> == PC.P) */
|
||||
export type PC_P<PC extends CurvePointCons<CurvePoint<any, any>>> = PC['ZERO'];
|
||||
export type PC_ANY = CurvePointCons<CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, any>>>>>>>>>>>;
|
||||
export interface CurveLengths {
|
||||
secretKey?: number;
|
||||
publicKey?: number;
|
||||
publicKeyUncompressed?: number;
|
||||
publicKeyHasPrefix?: boolean;
|
||||
signature?: number;
|
||||
seed?: number;
|
||||
}
|
||||
export type GroupConstructor<T> = {
|
||||
BASE: T;
|
||||
ZERO: T;
|
||||
};
|
||||
/** @deprecated */
|
||||
export type ExtendedGroupConstructor<T> = GroupConstructor<T> & {
|
||||
Fp: IField<any>;
|
||||
Fn: IField<bigint>;
|
||||
fromAffine(ap: AffinePoint<any>): T;
|
||||
};
|
||||
export type Mapper<T> = (i: T[]) => T[];
|
||||
export declare function negateCt<T extends {
|
||||
negate: () => T;
|
||||
}>(condition: boolean, item: T): T;
|
||||
/**
|
||||
* Takes a bunch of Projective Points but executes only one
|
||||
* inversion on all of them. Inversion is very slow operation,
|
||||
* so this improves performance massively.
|
||||
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
||||
*/
|
||||
export declare function normalizeZ<P extends CurvePoint<any, P>, PC extends CurvePointCons<P>>(c: PC, points: P[]): P[];
|
||||
/** Internal wNAF opts for specific W and scalarBits */
|
||||
export type WOpts = {
|
||||
windows: number;
|
||||
windowSize: number;
|
||||
mask: bigint;
|
||||
maxNumber: number;
|
||||
shiftBy: bigint;
|
||||
};
|
||||
/**
|
||||
* Elliptic curve multiplication of Point by scalar. Fragile.
|
||||
* Table generation takes **30MB of ram and 10ms on high-end CPU**,
|
||||
* but may take much longer on slow devices. Actual generation will happen on
|
||||
* first call of `multiply()`. By default, `BASE` point is precomputed.
|
||||
*
|
||||
* Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
||||
* Creates precomputation tables for fast multiplication:
|
||||
* - private scalar is split by fixed size windows of W bits
|
||||
* - every window point is collected from window's table & added to accumulator
|
||||
* - since windows are different, same point inside tables won't be accessed more than once per calc
|
||||
* - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
|
||||
* - +1 window is neccessary for wNAF
|
||||
* - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
|
||||
*
|
||||
* @todo Research returning 2d JS array of windows, instead of a single window.
|
||||
* This would allow windows to be in different memory locations
|
||||
*/
|
||||
export declare class wNAF<PC extends PC_ANY> {
|
||||
private readonly BASE;
|
||||
private readonly ZERO;
|
||||
private readonly Fn;
|
||||
readonly bits: number;
|
||||
constructor(Point: PC, bits: number);
|
||||
_unsafeLadder(elm: PC_P<PC>, n: bigint, p?: PC_P<PC>): PC_P<PC>;
|
||||
/**
|
||||
* Creates a wNAF precomputation window. Used for caching.
|
||||
* Default window size is set by `utils.precompute()` and is equal to 8.
|
||||
* Number of precomputed points depends on the curve size:
|
||||
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
|
||||
* - 𝑊 is the window size
|
||||
* - 𝑛 is the bitlength of the curve order.
|
||||
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
||||
* @param point Point instance
|
||||
* @param W window size
|
||||
* @returns precomputed point tables flattened to a single array
|
||||
*/
|
||||
private precomputeWindow;
|
||||
/**
|
||||
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
||||
* More compact implementation:
|
||||
* https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
|
||||
* @returns real and fake (for const-time) points
|
||||
*/
|
||||
private wNAF;
|
||||
/**
|
||||
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
|
||||
* @param acc accumulator point to add result of multiplication
|
||||
* @returns point
|
||||
*/
|
||||
private wNAFUnsafe;
|
||||
private getPrecomputes;
|
||||
cached(point: PC_P<PC>, scalar: bigint, transform?: Mapper<PC_P<PC>>): {
|
||||
p: PC_P<PC>;
|
||||
f: PC_P<PC>;
|
||||
};
|
||||
unsafe(point: PC_P<PC>, scalar: bigint, transform?: Mapper<PC_P<PC>>, prev?: PC_P<PC>): PC_P<PC>;
|
||||
createCache(P: PC_P<PC>, W: number): void;
|
||||
hasCache(elm: PC_P<PC>): boolean;
|
||||
}
|
||||
/**
|
||||
* Endomorphism-specific multiplication for Koblitz curves.
|
||||
* Cost: 128 dbl, 0-256 adds.
|
||||
*/
|
||||
export declare function mulEndoUnsafe<P extends CurvePoint<any, P>, PC extends CurvePointCons<P>>(Point: PC, point: P, k1: bigint, k2: bigint): {
|
||||
p1: P;
|
||||
p2: P;
|
||||
};
|
||||
/**
|
||||
* Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
||||
* 30x faster vs naive addition on L=4096, 10x faster than precomputes.
|
||||
* For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
|
||||
* Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
|
||||
* @param c Curve Point constructor
|
||||
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
||||
* @param points array of L curve points
|
||||
* @param scalars array of L scalars (aka secret keys / bigints)
|
||||
*/
|
||||
export declare function pippenger<P extends CurvePoint<any, P>, PC extends CurvePointCons<P>>(c: PC, fieldN: IField<bigint>, points: P[], scalars: bigint[]): P;
|
||||
/**
|
||||
* Precomputed multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
||||
* @param c Curve Point constructor
|
||||
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
||||
* @param points array of L curve points
|
||||
* @returns function which multiplies points with scaars
|
||||
*/
|
||||
export declare function precomputeMSMUnsafe<P extends CurvePoint<any, P>, PC extends CurvePointCons<P>>(c: PC, fieldN: IField<bigint>, points: P[], windowSize: number): (scalars: bigint[]) => P;
|
||||
/**
|
||||
* Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
||||
* Though generator can be different (Fp2 / Fp6 for BLS).
|
||||
*/
|
||||
export type BasicCurve<T> = {
|
||||
Fp: IField<T>;
|
||||
n: bigint;
|
||||
nBitLength?: number;
|
||||
nByteLength?: number;
|
||||
h: bigint;
|
||||
hEff?: bigint;
|
||||
Gx: T;
|
||||
Gy: T;
|
||||
allowInfinityPoint?: boolean;
|
||||
};
|
||||
/** @deprecated */
|
||||
export declare function validateBasic<FP, T>(curve: BasicCurve<FP> & T): Readonly<{
|
||||
readonly nBitLength: number;
|
||||
readonly nByteLength: number;
|
||||
} & BasicCurve<FP> & T & {
|
||||
p: bigint;
|
||||
}>;
|
||||
export type ValidCurveParams<T> = {
|
||||
p: bigint;
|
||||
n: bigint;
|
||||
h: bigint;
|
||||
a: T;
|
||||
b?: T;
|
||||
d?: T;
|
||||
Gx: T;
|
||||
Gy: T;
|
||||
};
|
||||
export type FpFn<T> = {
|
||||
Fp: IField<T>;
|
||||
Fn: IField<bigint>;
|
||||
};
|
||||
/** Validates CURVE opts and creates fields */
|
||||
export declare function _createCurveFields<T>(type: 'weierstrass' | 'edwards', CURVE: ValidCurveParams<T>, curveOpts?: Partial<FpFn<T>>, FpFnLE?: boolean): FpFn<T> & {
|
||||
CURVE: ValidCurveParams<T>;
|
||||
};
|
||||
//# sourceMappingURL=curve.d.ts.map
|
||||
@@ -0,0 +1,33 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
- 'v*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
|
||||
# This allows a subsequently queued workflow run to interrupt previous runs
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci.yml@2073dc8e1f9e172bf42daa3843c9dbd31af1e8cb # v6.0.0
|
||||
with:
|
||||
license-check: true
|
||||
lint: true
|
||||
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.safeDecodeAsync = exports.safeEncodeAsync = exports.safeDecode = exports.safeEncode = exports.decodeAsync = exports.encodeAsync = exports.decode = exports.encode = exports.safeParseAsync = exports.safeParse = exports.parseAsync = exports.parse = void 0;
|
||||
const core = __importStar(require("../core/index.cjs"));
|
||||
const errors_js_1 = require("./errors.cjs");
|
||||
exports.parse = core._parse(errors_js_1.ZodRealError);
|
||||
exports.parseAsync = core._parseAsync(errors_js_1.ZodRealError);
|
||||
exports.safeParse = core._safeParse(errors_js_1.ZodRealError);
|
||||
exports.safeParseAsync = core._safeParseAsync(errors_js_1.ZodRealError);
|
||||
// Codec functions
|
||||
exports.encode = core._encode(errors_js_1.ZodRealError);
|
||||
exports.decode = core._decode(errors_js_1.ZodRealError);
|
||||
exports.encodeAsync = core._encodeAsync(errors_js_1.ZodRealError);
|
||||
exports.decodeAsync = core._decodeAsync(errors_js_1.ZodRealError);
|
||||
exports.safeEncode = core._safeEncode(errors_js_1.ZodRealError);
|
||||
exports.safeDecode = core._safeDecode(errors_js_1.ZodRealError);
|
||||
exports.safeEncodeAsync = core._safeEncodeAsync(errors_js_1.ZodRealError);
|
||||
exports.safeDecodeAsync = core._safeDecodeAsync(errors_js_1.ZodRealError);
|
||||
@@ -0,0 +1 @@
|
||||
"use strict";var b=Object.defineProperty;var p=(s,l)=>b(s,"name",{value:l,configurable:!0});let t=!0;const n=typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{};let r=0;if(n.process&&n.process.env&&n.process.stdout){const{FORCE_COLOR:s,NODE_DISABLE_COLORS:l,NO_COLOR:c,TERM:o,COLORTERM:i}=n.process.env;l||c||s==="0"?t=!1:s==="1"||s==="2"||s==="3"?t=!0:o==="dumb"?t=!1:"CI"in n.process.env&&["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI","GITHUB_ACTIONS","BUILDKITE","DRONE"].some(g=>g in n.process.env)?t=!0:t=process.stdout.isTTY,t&&(process.platform==="win32"||i&&(i==="truecolor"||i==="24bit")?r=3:o&&(o.endsWith("-256color")||o.endsWith("256"))?r=2:r=1)}let a={enabled:t,supportLevel:r};function e(s,l,c=1){const o=`\x1B[${s}m`,i=`\x1B[${l}m`,g=new RegExp(`\\x1b\\[${l}m`,"g");return f=>a.enabled&&a.supportLevel>=c?o+(""+f).replace(g,o)+i:""+f}p(e,"kolorist");const u=e(30,39),d=e(33,39),O=e(90,39),C=e(92,39),L=e(95,39),R=e(96,39),y=e(44,49),I=e(100,49),h=e(103,49);exports.bgBlue=y,exports.bgGray=I,exports.bgLightYellow=h,exports.black=u,exports.gray=O,exports.lightCyan=R,exports.lightGreen=C,exports.lightMagenta=L,exports.options=a,exports.yellow=d;
|
||||
@@ -0,0 +1,7 @@
|
||||
declare function findNearestPackageData(basedir: string): {
|
||||
type?: "module" | "commonjs";
|
||||
};
|
||||
declare function getCachedData<T>(cache: Map<string, T>, basedir: string, originalBasedir: string): NonNullable<T> | undefined;
|
||||
declare function setCacheData<T>(cache: Map<string, T>, data: T, basedir: string, originalBasedir: string): void;
|
||||
|
||||
export { findNearestPackageData, getCachedData, setCacheData };
|
||||
@@ -0,0 +1,6 @@
|
||||
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
||||
function getDefaultExportFromCjs(x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
||||
}
|
||||
|
||||
export { commonjsGlobal as c, getDefaultExportFromCjs as g };
|
||||
@@ -0,0 +1,94 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getFirstSemanticOrSyntacticError = getFirstSemanticOrSyntacticError;
|
||||
const typescript_1 = require("typescript");
|
||||
/**
|
||||
* By default, diagnostics from the TypeScript compiler contain all errors - regardless of whether
|
||||
* they are related to generic ECMAScript standards, or TypeScript-specific constructs.
|
||||
*
|
||||
* Therefore, we filter out all diagnostics, except for the ones we explicitly want to consider when
|
||||
* the user opts in to throwing errors on semantic issues.
|
||||
*/
|
||||
function getFirstSemanticOrSyntacticError(program, ast) {
|
||||
try {
|
||||
const supportedSyntacticDiagnostics = allowlistSupportedDiagnostics(program.getSyntacticDiagnostics(ast));
|
||||
if (supportedSyntacticDiagnostics.length > 0) {
|
||||
return convertDiagnosticToSemanticOrSyntacticError(supportedSyntacticDiagnostics[0]);
|
||||
}
|
||||
const supportedSemanticDiagnostics = allowlistSupportedDiagnostics(program.getSemanticDiagnostics(ast));
|
||||
if (supportedSemanticDiagnostics.length > 0) {
|
||||
return convertDiagnosticToSemanticOrSyntacticError(supportedSemanticDiagnostics[0]);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
catch (e) {
|
||||
/**
|
||||
* TypeScript compiler has certain Debug.fail() statements in, which will cause the diagnostics
|
||||
* retrieval above to throw.
|
||||
*
|
||||
* E.g. from ast-alignment-tests
|
||||
* "Debug Failure. Shouldn't ever directly check a JsxOpeningElement"
|
||||
*
|
||||
* For our current use-cases this is undesired behavior, so we just suppress it
|
||||
* and log a warning.
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
console.warn(`Warning From TSC: "${e.message}`); // eslint-disable-line no-console
|
||||
/* istanbul ignore next */
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
function allowlistSupportedDiagnostics(diagnostics) {
|
||||
return diagnostics.filter(diagnostic => {
|
||||
switch (diagnostic.code) {
|
||||
case 1013: // "A rest parameter or binding pattern may not have a trailing comma."
|
||||
case 1014: // "A rest parameter must be last in a parameter list."
|
||||
case 1044: // "'{0}' modifier cannot appear on a module or namespace element."
|
||||
case 1045: // "A '{0}' modifier cannot be used with an interface declaration."
|
||||
case 1048: // "A rest parameter cannot have an initializer."
|
||||
case 1049: // "A 'set' accessor must have exactly one parameter."
|
||||
case 1070: // "'{0}' modifier cannot appear on a type member."
|
||||
case 1071: // "'{0}' modifier cannot appear on an index signature."
|
||||
case 1085: // "Octal literals are not available when targeting ECMAScript 5 and higher. Use the syntax '{0}'."
|
||||
case 1090: // "'{0}' modifier cannot appear on a parameter."
|
||||
case 1096: // "An index signature must have exactly one parameter."
|
||||
case 1097: // "'{0}' list cannot be empty."
|
||||
case 1098: // "Type parameter list cannot be empty."
|
||||
case 1099: // "Type argument list cannot be empty."
|
||||
case 1117: // "An object literal cannot have multiple properties with the same name in strict mode."
|
||||
case 1121: // "Octal literals are not allowed in strict mode."
|
||||
case 1123: // "Variable declaration list cannot be empty."
|
||||
case 1141: // "String literal expected."
|
||||
case 1162: // "An object member cannot be declared optional."
|
||||
case 1164: // "Computed property names are not allowed in enums."
|
||||
case 1172: // "'extends' clause already seen."
|
||||
case 1173: // "'extends' clause must precede 'implements' clause."
|
||||
case 1175: // "'implements' clause already seen."
|
||||
case 1176: // "Interface declaration cannot have 'implements' clause."
|
||||
case 1190: // "The variable declaration of a 'for...of' statement cannot have an initializer."
|
||||
case 1196: // "Catch clause variable type annotation must be 'any' or 'unknown' if specified."
|
||||
case 1200: // "Line terminator not permitted before arrow."
|
||||
case 1206: // "Decorators are not valid here."
|
||||
case 1211: // "A class declaration without the 'default' modifier must have a name."
|
||||
case 1242: // "'abstract' modifier can only appear on a class, method, or property declaration."
|
||||
case 1246: // "An interface property cannot have an initializer."
|
||||
case 1255: // "A definite assignment assertion '!' is not permitted in this context."
|
||||
case 1308: // "'await' expression is only allowed within an async function."
|
||||
case 2364: // "The left-hand side of an assignment expression must be a variable or a property access."
|
||||
case 2369: // "A parameter property is only allowed in a constructor implementation."
|
||||
case 2452: // "An enum member cannot have a numeric name."
|
||||
case 2462: // "A rest element must be last in a destructuring pattern."
|
||||
case 8017: // "Octal literal types must use ES2015 syntax. Use the syntax '{0}'."
|
||||
case 17012: // "'{0}' is not a valid meta-property for keyword '{1}'. Did you mean '{2}'?"
|
||||
case 17013: // "Meta-property '{0}' is only allowed in the body of a function declaration, function expression, or constructor."
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
function convertDiagnosticToSemanticOrSyntacticError(diagnostic) {
|
||||
return {
|
||||
...diagnostic,
|
||||
message: (0, typescript_1.flattenDiagnosticMessageText)(diagnostic.messageText, typescript_1.sys.newLine),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,757 @@
|
||||
<a href="https://dotenvx.com/?utm_source=github&utm_medium=readme&utm_campaign=motdotla-dotenv&utm_content=banner"><img src="https://dotenvx.com/dotenv-banner.png" alt="dotenvx" /></a>
|
||||
|
||||
# dotenv [](https://www.npmjs.com/package/dotenv) [](https://www.npmjs.com/package/dotenv)
|
||||
|
||||
<img src="https://raw.githubusercontent.com/motdotla/dotenv/master/dotenv.svg" alt="dotenv" align="right" width="200" />
|
||||
|
||||
Dotenv es un módulo sin dependencias que carga variables de entorno desde un archivo `.env` en [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env). Guardar la configuración en el entorno, separada del código, se basa en la metodología de [The Twelve-Factor App](https://12factor.net/config).
|
||||
|
||||
[Ver el tutorial](https://www.youtube.com/watch?v=YtkZR0NFd1g)
|
||||
|
||||
|
||||
|
||||
## Uso
|
||||
|
||||
Instálalo.
|
||||
|
||||
```sh
|
||||
npm install dotenv --save
|
||||
```
|
||||
|
||||
Crea un archivo `.env` en la raíz de tu proyecto:
|
||||
|
||||
```ini
|
||||
# .env
|
||||
S3_BUCKET="YOURS3BUCKET"
|
||||
SECRET_KEY="YOURSECRETKEYGOESHERE"
|
||||
```
|
||||
|
||||
Y lo antes posible en tu aplicación, importa y configura dotenv:
|
||||
|
||||
```javascript
|
||||
// index.js
|
||||
require('dotenv').config() // o import 'dotenv/config' si usas ES6
|
||||
...
|
||||
console.log(process.env) // elimínalo después de confirmar que funciona
|
||||
```
|
||||
```sh
|
||||
$ node index.js
|
||||
◇ injected env (14) from .env
|
||||
```
|
||||
|
||||
Eso es todo. `process.env` ahora tiene las claves y valores que definiste en tu archivo `.env`.
|
||||
|
||||
|
||||
|
||||
## Uso con agentes
|
||||
|
||||
Instala este repositorio como paquete de habilidad para tu agente:
|
||||
|
||||
```sh
|
||||
npx skills add motdotla/dotenv
|
||||
```
|
||||
```sh
|
||||
# luego dile a Claude/Codex cosas como:
|
||||
configura dotenv
|
||||
actualiza dotenv a dotenvx
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Avanzado
|
||||
|
||||
<details><summary>ES6</summary><br>
|
||||
|
||||
Importa con [ES6](#como-uso-dotenv-con-import):
|
||||
|
||||
```javascript
|
||||
import 'dotenv/config'
|
||||
```
|
||||
|
||||
Import con ES6 si necesitas establecer opciones de configuración:
|
||||
|
||||
```javascript
|
||||
import dotenv from 'dotenv'
|
||||
dotenv.config({ path: '/custom/path/to/.env' })
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>bun</summary><br>
|
||||
|
||||
```sh
|
||||
bun add dotenv
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>yarn</summary><br>
|
||||
|
||||
```sh
|
||||
yarn add dotenv
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>pnpm</summary><br>
|
||||
|
||||
```sh
|
||||
pnpm add dotenv
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>Monorepos</summary><br>
|
||||
|
||||
Para monorepos con una estructura como `apps/backend/app.js`, coloca el archivo `.env` en la raíz de la carpeta donde corre tu proceso `app.js`.
|
||||
|
||||
```ini
|
||||
# app/backend/.env
|
||||
S3_BUCKET="YOURS3BUCKET"
|
||||
SECRET_KEY="YOURSECRETKEYGOESHERE"
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>Valores Multilínea</summary><br>
|
||||
|
||||
Si necesitas variables multilínea, por ejemplo claves privadas, ya son compatibles (`>= v15.0.0`) con saltos de línea:
|
||||
|
||||
```ini
|
||||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||
...
|
||||
Kh9NV...
|
||||
...
|
||||
-----END RSA PRIVATE KEY-----"
|
||||
```
|
||||
|
||||
Como alternativa, puedes usar comillas dobles y el carácter `\n`:
|
||||
|
||||
```ini
|
||||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nKh9NV...\n-----END RSA PRIVATE KEY-----\n"
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>Comentarios</summary><br>
|
||||
|
||||
Puedes agregar comentarios en su propia línea o al final de una línea:
|
||||
|
||||
```ini
|
||||
# Este es un comentario
|
||||
SECRET_KEY=YOURSECRETKEYGOESHERE # comentario
|
||||
SECRET_HASH="something-with-a-#-hash"
|
||||
```
|
||||
|
||||
Los comentarios empiezan donde aparece `#`, así que si tu valor contiene `#` debes envolverlo entre comillas. Este es un cambio incompatible desde `>= v15.0.0`.
|
||||
|
||||
</details>
|
||||
<details><summary>Análisis</summary><br>
|
||||
|
||||
El motor que analiza el contenido del archivo de variables de entorno está disponible para su uso. Acepta un String o Buffer y devuelve un objeto con las claves y valores analizados.
|
||||
|
||||
```javascript
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('BASIC=basic')
|
||||
const config = dotenv.parse(buf) // devolverá un objeto
|
||||
console.log(typeof config, config) // objeto { BASIC : 'basic' }
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>Precarga</summary><br>
|
||||
|
||||
> Nota: considera usar [`dotenvx`](https://github.com/dotenvx/dotenvx) en lugar de precargar. Ahora lo hago (y lo recomiendo).
|
||||
>
|
||||
> Cumple el mismo propósito (no necesitas hacer require y cargar dotenv), agrega mejor depuración y funciona con CUALQUIER lenguaje, framework o plataforma. – [motdotla](https://not.la)
|
||||
|
||||
Puedes usar la [opción de línea de comandos](https://nodejs.org/api/cli.html#-r---require-module) `--require` (`-r`) para precargar dotenv. Con esto no necesitas requerir ni cargar dotenv en el código de tu aplicación.
|
||||
|
||||
```bash
|
||||
$ node -r dotenv/config your_script.js
|
||||
```
|
||||
|
||||
Las opciones de configuración de abajo se aceptan como argumentos de línea de comandos en el formato `dotenv_config_<option>=value`
|
||||
|
||||
```bash
|
||||
$ node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env dotenv_config_debug=true
|
||||
```
|
||||
|
||||
Además, puedes usar variables de entorno para establecer opciones de configuración. Los argumentos de línea de comandos tienen prioridad.
|
||||
|
||||
```bash
|
||||
$ DOTENV_CONFIG_<OPTION>=value node -r dotenv/config your_script.js
|
||||
```
|
||||
|
||||
```bash
|
||||
$ DOTENV_CONFIG_ENCODING=latin1 DOTENV_CONFIG_DEBUG=true node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>Expansión de Variables</summary><br>
|
||||
|
||||
Usa [dotenvx](https://github.com/dotenvx/dotenvx) para expansión de variables.
|
||||
|
||||
Referencia y expande variables que ya existen en tu máquina para usarlas en tu archivo .env.
|
||||
|
||||
```ini
|
||||
# .env
|
||||
USERNAME="username"
|
||||
DATABASE_URL="postgres://${USERNAME}@localhost/my_database"
|
||||
```
|
||||
```js
|
||||
// index.js
|
||||
console.log('DATABASE_URL', process.env.DATABASE_URL)
|
||||
```
|
||||
```sh
|
||||
$ dotenvx run --debug -- node index.js
|
||||
⟐ injected env (2) from .env · dotenvx@1.59.1
|
||||
DATABASE_URL postgres://username@localhost/my_database
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>Sustitución de Comandos</summary><br>
|
||||
|
||||
Usa [dotenvx](https://github.com/dotenvx/dotenvx) para sustitución de comandos.
|
||||
|
||||
Agrega la salida de un comando a una de tus variables en tu archivo .env.
|
||||
|
||||
```ini
|
||||
# .env
|
||||
DATABASE_URL="postgres://$(whoami)@localhost/my_database"
|
||||
```
|
||||
```js
|
||||
// index.js
|
||||
console.log('DATABASE_URL', process.env.DATABASE_URL)
|
||||
```
|
||||
```sh
|
||||
$ dotenvx run --debug -- node index.js
|
||||
⟐ injected env (1) from .env · dotenvx@1.59.1
|
||||
DATABASE_URL postgres://yourusername@localhost/my_database
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>Cifrado</summary><br>
|
||||
|
||||
Usa [dotenvx](https://github.com/dotenvx/dotenvx) para cifrado.
|
||||
|
||||
Agrega cifrado a tus archivos `.env` con un solo comando.
|
||||
|
||||
```
|
||||
$ dotenvx set HELLO Production -f .env.production
|
||||
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
|
||||
|
||||
$ DOTENV_PRIVATE_KEY_PRODUCTION="<.env.production private key>" dotenvx run -- node index.js
|
||||
⟐ injected env (2) from .env.production · dotenvx@1.59.1
|
||||
Hello Production
|
||||
```
|
||||
|
||||
[más información](https://github.com/dotenvx/dotenvx?tab=readme-ov-file#encryption)
|
||||
|
||||
</details>
|
||||
<details><summary>Múltiples Entornos</summary><br>
|
||||
|
||||
Usa [dotenvx](https://github.com/dotenvx/dotenvx) para administrar múltiples entornos.
|
||||
|
||||
Ejecuta cualquier entorno localmente. Crea un archivo `.env.ENVIRONMENT` y usa `-f` para cargarlo. Es simple y flexible.
|
||||
|
||||
```bash
|
||||
$ echo "HELLO=production" > .env.production
|
||||
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
|
||||
|
||||
$ dotenvx run -f=.env.production -- node index.js
|
||||
Hello production
|
||||
> ^^
|
||||
```
|
||||
|
||||
o con múltiples archivos .env
|
||||
|
||||
```bash
|
||||
$ echo "HELLO=local" > .env.local
|
||||
$ echo "HELLO=World" > .env
|
||||
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
|
||||
|
||||
$ dotenvx run -f=.env.local -f=.env -- node index.js
|
||||
Hello local
|
||||
```
|
||||
|
||||
[más ejemplos de entornos](https://dotenvx.com/docs/quickstart/environments?utm_source=github&utm_medium=readme&utm_campaign=motdotla-dotenv&utm_content=docs-environments)
|
||||
|
||||
</details>
|
||||
<details><summary>Producción</summary><br>
|
||||
|
||||
Usa [dotenvx](https://github.com/dotenvx/dotenvx) para despliegues en producción.
|
||||
|
||||
Crea un archivo `.env.production`.
|
||||
|
||||
```sh
|
||||
$ echo "HELLO=production" > .env.production
|
||||
```
|
||||
|
||||
Cífralo.
|
||||
|
||||
```sh
|
||||
$ dotenvx encrypt -f .env.production
|
||||
```
|
||||
|
||||
Configura `DOTENV_PRIVATE_KEY_PRODUCTION` (está en `.env.keys`) en tu servidor.
|
||||
|
||||
```
|
||||
$ heroku config:set DOTENV_PRIVATE_KEY_PRODUCTION=value
|
||||
```
|
||||
|
||||
Haz commit de tu archivo `.env.production` y despliega.
|
||||
|
||||
```
|
||||
$ git add .env.production
|
||||
$ git commit -m "encrypted .env.production"
|
||||
$ git push heroku main
|
||||
```
|
||||
|
||||
Dotenvx descifrará e inyectará los secretos en runtime usando `dotenvx run -- node index.js`.
|
||||
|
||||
</details>
|
||||
<details><summary>Sincronización</summary><br>
|
||||
|
||||
Usa [dotenvx](https://github.com/dotenvx/dotenvx) para sincronizar tus archivos .env.
|
||||
|
||||
Cífralos con `dotenvx encrypt -f .env` e inclúyelos de forma segura en el control de código fuente. Tus secretos se sincronizan de forma segura con git.
|
||||
|
||||
Esto sigue las reglas de Twelve-Factor App al generar una clave de descifrado separada del código.
|
||||
|
||||
</details>
|
||||
<details><summary>Más Ejemplos</summary><br>
|
||||
|
||||
Mira [ejemplos](https://github.com/dotenv-org/examples) de uso de dotenv con distintos frameworks, lenguajes y configuraciones.
|
||||
|
||||
* [nodejs](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs)
|
||||
* [nodejs (debug on)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs-debug)
|
||||
* [nodejs (override on)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nodejs-override)
|
||||
* [nodejs (processEnv override)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-custom-target)
|
||||
* [esm](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-esm)
|
||||
* [esm (preload)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-esm-preload)
|
||||
* [typescript](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript)
|
||||
* [typescript parse](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript-parse)
|
||||
* [typescript config](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-typescript-config)
|
||||
* [webpack](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-webpack)
|
||||
* [webpack (plugin)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-webpack2)
|
||||
* [react](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-react)
|
||||
* [react (typescript)](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-react-typescript)
|
||||
* [express](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-express)
|
||||
* [nestjs](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-nestjs)
|
||||
* [fastify](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-fastify)
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
## Preguntas Frecuentes
|
||||
|
||||
<details><summary>¿Debo hacer commit de mi archivo `.env`?</summary><br/>
|
||||
|
||||
No.
|
||||
|
||||
A menos que lo cifres con [dotenvx](https://github.com/dotenvx/dotenvx). En ese caso sí lo recomendamos.
|
||||
|
||||
</details>
|
||||
<details><summary>¿Qué pasa con la expansión de variables?</summary><br/>
|
||||
|
||||
Usa [dotenvx](https://github.com/dotenvx/dotenvx).
|
||||
|
||||
</details>
|
||||
<details><summary>¿Debo tener múltiples archivos `.env`?</summary><br/>
|
||||
|
||||
Recomendamos crear un archivo `.env` por entorno. Usa `.env` para local/desarrollo, `.env.production` para producción, etc. Esto sigue los principios de Twelve-Factor porque cada uno pertenece de forma independiente a su entorno. Evita configuraciones personalizadas con herencia (`.env.production` hereda valores de `.env`, por ejemplo). Es mejor duplicar valores cuando sea necesario en cada archivo `.env.environment`.
|
||||
|
||||
> En una app twelve-factor, las variables de entorno son controles granulares, totalmente ortogonales entre sí. Nunca se agrupan como “entornos”; en cambio, se administran de forma independiente por despliegue. Este modelo escala de forma natural a medida que la app crece en más despliegues a lo largo del tiempo.
|
||||
>
|
||||
> – [The Twelve-Factor App](http://12factor.net/config)
|
||||
|
||||
Además, recomendamos usar [dotenvx](https://github.com/dotenvx/dotenvx) para cifrarlos y administrarlos.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>¿Cómo uso dotenv con `import`?</summary><br/>
|
||||
|
||||
Simplemente..
|
||||
|
||||
```javascript
|
||||
// index.mjs (ESM)
|
||||
import 'dotenv/config' // ver https://github.com/motdotla/dotenv#como-uso-dotenv-con-import
|
||||
import express from 'express'
|
||||
```
|
||||
|
||||
Un poco de contexto..
|
||||
|
||||
> Cuando ejecutas un módulo que contiene una declaración `import`, primero se cargan los módulos importados y luego se ejecuta cada cuerpo de módulo en un recorrido en profundidad del grafo de dependencias, evitando ciclos al omitir lo que ya se ejecutó.
|
||||
>
|
||||
> – [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/)
|
||||
|
||||
¿Qué significa esto en lenguaje simple? Que parece que lo siguiente debería funcionar, pero no funciona.
|
||||
|
||||
`errorReporter.mjs`:
|
||||
```js
|
||||
class Client {
|
||||
constructor (apiKey) {
|
||||
console.log('apiKey', apiKey)
|
||||
|
||||
this.apiKey = apiKey
|
||||
}
|
||||
}
|
||||
|
||||
export default new Client(process.env.API_KEY)
|
||||
```
|
||||
`index.mjs`:
|
||||
```js
|
||||
// Nota: esto es INCORRECTO y no funcionará
|
||||
import * as dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
import errorReporter from './errorReporter.mjs' // process.env.API_KEY estará vacío
|
||||
```
|
||||
|
||||
`process.env.API_KEY` estará vacío.
|
||||
|
||||
En su lugar, `index.mjs` debería escribirse así..
|
||||
|
||||
```js
|
||||
import 'dotenv/config'
|
||||
|
||||
import errorReporter from './errorReporter.mjs'
|
||||
```
|
||||
|
||||
¿Tiene sentido? Es un poco poco intuitivo, pero así funciona la importación de módulos ES6. Aquí tienes un [ejemplo funcional de este problema](https://github.com/dotenv-org/examples/tree/master/usage/dotenv-es6-import-pitfall).
|
||||
|
||||
Hay dos alternativas a este enfoque:
|
||||
|
||||
1. Precargar con dotenvx: `dotenvx run -- node index.js` (_Nota: con este enfoque no necesitas `import` dotenv_)
|
||||
2. Crear un archivo separado que ejecute `config` primero, como se indica en [este comentario de #133](https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822)
|
||||
</details>
|
||||
|
||||
<details><summary>¿Puedo personalizar/escribir plugins para dotenv?</summary><br/>
|
||||
|
||||
Sí. `dotenv.config()` devuelve un objeto que representa el archivo `.env` analizado. Con eso tienes lo necesario para seguir estableciendo valores en `process.env`. Por ejemplo:
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const variableExpansion = require('dotenv-expand')
|
||||
const myEnv = dotenv.config()
|
||||
variableExpansion(myEnv)
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>¿Qué reglas sigue el motor de análisis?</summary><br/>
|
||||
|
||||
El motor de análisis actualmente soporta las siguientes reglas:
|
||||
|
||||
- `BASIC=basic` se convierte en `{BASIC: 'basic'}`
|
||||
- las líneas vacías se omiten
|
||||
- las líneas que empiezan con `#` se tratan como comentarios
|
||||
- `#` marca el inicio de un comentario (a menos que el valor esté entre comillas)
|
||||
- los valores vacíos se convierten en cadenas vacías (`EMPTY=` pasa a `{EMPTY: ''}`)
|
||||
- las comillas internas se conservan (piensa en JSON) (`JSON={"foo": "bar"}` se convierte en `{JSON:"{\"foo\": \"bar\"}"`)
|
||||
- se elimina el espacio al principio y al final de valores sin comillas (más en [`trim`](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/trim)) (`FOO= some value ` pasa a `{FOO: 'some value'}`)
|
||||
- los valores con comillas simples o dobles se escapan (`SINGLE_QUOTE='quoted'` pasa a `{SINGLE_QUOTE: "quoted"}`)
|
||||
- los valores entre comillas simples o dobles mantienen los espacios en ambos extremos (`FOO=" some value "` pasa a `{FOO: ' some value '}`)
|
||||
- los valores entre comillas dobles expanden saltos de línea (`MULTILINE="new\nline"` pasa a
|
||||
|
||||
```
|
||||
{MULTILINE: 'new
|
||||
line'}
|
||||
```
|
||||
|
||||
- se admiten backticks (`` BACKTICK_KEY=`This has 'single' and "double" quotes inside of it.` ``)
|
||||
|
||||
</details>
|
||||
<details><summary>¿Qué hay de sincronizar y proteger archivos .env?</summary><br/>
|
||||
|
||||
Usa [dotenvx](https://github.com/dotenvx/dotenvx) para habilitar la sincronización de archivos .env cifrados sobre git.
|
||||
|
||||
</details>
|
||||
<details><summary>¿Qué pasa si hago commit accidentalmente de mi archivo `.env`?</summary><br/>
|
||||
|
||||
Elimínalo, [borra el historial de git](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository) y luego instala el [hook de pre-commit de git](https://github.com/dotenvx/dotenvx#pre-commit) para evitar que vuelva a pasar.
|
||||
|
||||
```
|
||||
npm i -g @dotenvx/dotenvx
|
||||
dotenvx precommit --install
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>¿Qué pasa con variables de entorno que ya estaban definidas?</summary><br/>
|
||||
|
||||
Por defecto, nunca modificamos variables de entorno que ya estén definidas. En particular, si hay una variable en tu archivo `.env` que colisiona con una ya existente en tu entorno, esa variable se omite.
|
||||
|
||||
Si en cambio quieres sobrescribir `process.env`, usa la opción `override`.
|
||||
|
||||
```javascript
|
||||
require('dotenv').config({ override: true })
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>¿Cómo evito incluir mi archivo `.env` en un build de Docker?</summary><br/>
|
||||
|
||||
Usa el [hook de prebuild para docker](https://dotenvx.com/docs/features/prebuild?utm_source=github&utm_medium=readme&utm_campaign=motdotla-dotenv&utm_content=docs-prebuild).
|
||||
|
||||
```bash
|
||||
# Dockerfile
|
||||
...
|
||||
RUN curl -fsS https://dotenvx.sh/ | sh
|
||||
...
|
||||
RUN dotenvx prebuild
|
||||
CMD ["dotenvx", "run", "--", "node", "index.js"]
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary>¿Por qué no aparecen mis variables de entorno en React?</summary><br/>
|
||||
|
||||
Tu código React corre en Webpack, donde el módulo `fs` o incluso el global `process` no son accesibles de forma predeterminada. `process.env` solo se puede inyectar mediante configuración de Webpack.
|
||||
|
||||
Si usas [`react-scripts`](https://www.npmjs.com/package/react-scripts), distribuido vía [`create-react-app`](https://create-react-app.dev/), ya incluye dotenv, pero con una condición. Antepone `REACT_APP_` a tus variables de entorno. Mira [este stack overflow](https://stackoverflow.com/questions/42182577/is-it-possible-to-use-dotenv-in-a-react-project) para más detalles.
|
||||
|
||||
Si usas otros frameworks (por ejemplo, Next.js, Gatsby...), debes revisar su documentación para inyectar variables de entorno en el cliente.
|
||||
|
||||
</details>
|
||||
<details><summary>¿Por qué el archivo `.env` no carga mis variables de entorno correctamente?</summary><br/>
|
||||
|
||||
Lo más probable es que tu archivo `.env` no esté en el lugar correcto. [Mira este stack overflow](https://stackoverflow.com/questions/42335016/dotenv-file-is-not-loading-environment-variables).
|
||||
|
||||
Activa el modo debug y prueba de nuevo..
|
||||
|
||||
```js
|
||||
require('dotenv').config({ debug: true })
|
||||
```
|
||||
|
||||
Recibirás un error útil en la consola.
|
||||
|
||||
</details>
|
||||
<details><summary>¿Por qué recibo el error `Module not found: Error: Can't resolve 'crypto|os|path'`?</summary><br/>
|
||||
|
||||
Estás usando dotenv en el front-end y no incluiste un polyfill. Webpack < 5 solía incluirlos. Haz lo siguiente:
|
||||
|
||||
```bash
|
||||
npm install node-polyfill-webpack-plugin
|
||||
```
|
||||
|
||||
Configura tu `webpack.config.js` con algo como lo siguiente.
|
||||
|
||||
```js
|
||||
require('dotenv').config()
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require('webpack')
|
||||
|
||||
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
plugins: [
|
||||
new NodePolyfillPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
HELLO: JSON.stringify(process.env.HELLO)
|
||||
}
|
||||
}),
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
Como alternativa, usa [dotenv-webpack](https://github.com/mrsteele/dotenv-webpack), que hace esto y más por detrás.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
## Documentación
|
||||
|
||||
Dotenv expone tres funciones:
|
||||
|
||||
* `config`
|
||||
* `parse`
|
||||
* `populate`
|
||||
|
||||
### Config
|
||||
|
||||
`config` leerá tu archivo `.env`, analizará su contenido, lo asignará a
|
||||
[`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env),
|
||||
y devolverá un objeto con una clave `parsed` con el contenido cargado o una clave `error` si falla.
|
||||
|
||||
```js
|
||||
const result = dotenv.config()
|
||||
|
||||
if (result.error) {
|
||||
throw result.error
|
||||
}
|
||||
|
||||
console.log(result.parsed)
|
||||
```
|
||||
|
||||
También puedes pasar opciones a `config`.
|
||||
|
||||
#### Opciones
|
||||
|
||||
##### path
|
||||
|
||||
Por defecto: `path.resolve(process.cwd(), '.env')`
|
||||
|
||||
Especifica una ruta personalizada si tu archivo de variables de entorno está en otro lugar.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ path: '/custom/path/to/.env' })
|
||||
```
|
||||
|
||||
Por defecto, `config` buscará un archivo llamado .env en el directorio de trabajo actual.
|
||||
|
||||
Pasa múltiples archivos como un arreglo; se analizarán en orden y se combinarán con `process.env` (o `option.processEnv`, si se define). El primer valor asignado a una variable prevalece, salvo que `options.override` esté activo; en ese caso prevalece el último. Si un valor ya existe en `process.env` y `options.override` NO está activo, no se hará ningún cambio en ese valor.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ path: ['.env.local', '.env'] })
|
||||
```
|
||||
|
||||
##### quiet
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Suprime el mensaje de logging en tiempo de ejecución.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
require('dotenv').config({ quiet: false }) // cambia a true para suprimir
|
||||
console.log(`Hello ${process.env.HELLO}`)
|
||||
```
|
||||
|
||||
```ini
|
||||
# .env
|
||||
HELLO=World
|
||||
```
|
||||
|
||||
```sh
|
||||
$ node index.js
|
||||
Hola Mundo
|
||||
```
|
||||
|
||||
##### encoding
|
||||
|
||||
Por defecto: `utf8`
|
||||
|
||||
Especifica la codificación del archivo que contiene variables de entorno.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ encoding: 'latin1' })
|
||||
```
|
||||
|
||||
##### debug
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Activa logs para depurar por qué ciertas claves o valores no se establecen como esperas.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ debug: process.env.DEBUG })
|
||||
```
|
||||
|
||||
##### override
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Sobrescribe cualquier variable de entorno ya definida en tu máquina con valores de tus archivos .env. Si se proporcionan múltiples archivos en `option.path`, `override` también aplica al combinar cada archivo con el siguiente. Sin `override`, prevalece el primer valor. Con `override`, prevalece el último.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ override: true })
|
||||
```
|
||||
|
||||
##### processEnv
|
||||
|
||||
Por defecto: `process.env`
|
||||
|
||||
Especifica un objeto donde escribir tus variables de entorno. Por defecto usa `process.env`.
|
||||
|
||||
```js
|
||||
const myObject = {}
|
||||
require('dotenv').config({ processEnv: myObject })
|
||||
|
||||
console.log(myObject) // valores desde .env
|
||||
console.log(process.env) // esto no se modificó ni escribió
|
||||
```
|
||||
|
||||
### Parse
|
||||
|
||||
El motor que analiza el contenido de tu archivo de variables
|
||||
de entorno está disponible para usar. Acepta un String o Buffer y devuelve
|
||||
un objeto con las claves y valores analizados.
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('BASIC=basic')
|
||||
const config = dotenv.parse(buf) // devolverá un objeto
|
||||
console.log(typeof config, config) // objeto { BASIC : 'basic' }
|
||||
```
|
||||
|
||||
#### Opciones
|
||||
|
||||
##### debug
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Activa logs para depurar por qué ciertas claves o valores no se establecen como esperas.
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('hola mundo')
|
||||
const opt = { debug: true }
|
||||
const config = dotenv.parse(buf, opt)
|
||||
// espera un mensaje de depuración porque el buffer no tiene formato KEY=VAL
|
||||
```
|
||||
|
||||
### Populate
|
||||
|
||||
El motor que carga el contenido de tu archivo .env en `process.env` está disponible para su uso. Acepta un objetivo, una fuente y opciones. Es útil para usuarios avanzados que quieren proveer sus propios objetos.
|
||||
|
||||
Por ejemplo, personalizando la fuente:
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const parsed = { HELLO: 'world' }
|
||||
|
||||
dotenv.populate(process.env, parsed)
|
||||
|
||||
console.log(process.env.HELLO) // world
|
||||
```
|
||||
|
||||
Por ejemplo, personalizando la fuente Y el objetivo:
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const parsed = { HELLO: 'universe' }
|
||||
const target = { HELLO: 'world' } // objeto inicial
|
||||
|
||||
dotenv.populate(target, parsed, { override: true, debug: true })
|
||||
|
||||
console.log(target) // { HELLO: 'universe' }
|
||||
```
|
||||
|
||||
#### opciones
|
||||
|
||||
##### Debug
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Activa logs para depurar por qué ciertas claves o valores no se están cargando como esperas.
|
||||
|
||||
##### override
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Sobrescribe cualquier variable de entorno que ya haya sido definida.
|
||||
|
||||
|
||||
|
||||
## CHANGELOG
|
||||
|
||||
Ver [CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
|
||||
|
||||
## ¿Quién usa dotenv?
|
||||
|
||||
[Estos módulos de npm dependen de él.](https://www.npmjs.com/browse/depended/dotenv)
|
||||
|
||||
Los proyectos que lo extienden suelen usar la [palabra clave "dotenv" en npm](https://www.npmjs.com/search?q=keywords:dotenv).
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
import { s as setupBaseEnvironment, r as runBaseTests } from '../chunks/base.B6Opl8PE.js';
|
||||
import { w as workerInit } from '../chunks/init-threads.6kl1khcL.js';
|
||||
import 'node:vm';
|
||||
import '@vitest/spy';
|
||||
import '../chunks/index.DXx9Dtk7.js';
|
||||
import '@vitest/expect';
|
||||
import 'node:async_hooks';
|
||||
import '../chunks/setup-common.DYx3LtFI.js';
|
||||
import '../chunks/coverage.CTzCuANN.js';
|
||||
import '@vitest/snapshot';
|
||||
import '@vitest/utils/timers';
|
||||
import '../chunks/utils.BX5Fg8C4.js';
|
||||
import '../chunks/rpc.MzXet3jl.js';
|
||||
import '../chunks/index.Chj8NDwU.js';
|
||||
import '../chunks/test.DNmyFkvJ.js';
|
||||
import '@vitest/runner';
|
||||
import '@vitest/utils/helpers';
|
||||
import '../chunks/benchmark.CX_oY03V.js';
|
||||
import '@vitest/runner/utils';
|
||||
import '@vitest/utils/error';
|
||||
import 'pathe';
|
||||
import '@vitest/utils/offset';
|
||||
import '@vitest/utils/source-map';
|
||||
import '../chunks/_commonjsHelpers.D26ty3Ew.js';
|
||||
import '../chunks/init.k9zZ9sLh.js';
|
||||
import 'node:fs';
|
||||
import 'node:module';
|
||||
import 'node:url';
|
||||
import 'vite/module-runner';
|
||||
import '../chunks/startVitestModuleRunner.DB-7oCpn.js';
|
||||
import '../chunks/modules.BJuCwlRJ.js';
|
||||
import '../path.js';
|
||||
import 'node:path';
|
||||
import '../module-evaluator.js';
|
||||
import '../chunks/traces.DT5aQ62U.js';
|
||||
import '@vitest/mocker';
|
||||
import '@vitest/mocker/redirect';
|
||||
import '../chunks/index.DC7d2Pf8.js';
|
||||
import 'node:console';
|
||||
import '@vitest/utils/serialize';
|
||||
import 'tinyrainbow';
|
||||
import '../chunks/inspector.CvyFGlXm.js';
|
||||
import '../chunks/evaluatedModules.Dg1zASAC.js';
|
||||
import '../chunks/nativeModuleRunner.BIakptoF.js';
|
||||
import '../chunks/index.BCY_7LL2.js';
|
||||
import 'node:process';
|
||||
import 'node:fs/promises';
|
||||
import 'node:assert';
|
||||
import 'node:v8';
|
||||
import 'node:util';
|
||||
import 'node:perf_hooks';
|
||||
import 'node:timers';
|
||||
import 'node:timers/promises';
|
||||
import '@vitest/utils/constants';
|
||||
import '../chunks/index.DdgEv5B1.js';
|
||||
import 'expect-type';
|
||||
import 'node:worker_threads';
|
||||
|
||||
workerInit({
|
||||
runTests: runBaseTests,
|
||||
setup: setupBaseEnvironment
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const compare = require('./compare')
|
||||
const rcompare = (a, b, loose) => compare(b, a, loose)
|
||||
module.exports = rcompare
|
||||
@@ -0,0 +1,12 @@
|
||||
export declare enum ElementFlags {
|
||||
None = 0,
|
||||
Required = 1,
|
||||
Optional = 2,
|
||||
Rest = 4,
|
||||
Variadic = 8,
|
||||
Fixed = 3,
|
||||
Variable = 12,
|
||||
NonRequired = 14,
|
||||
NonRest = 11
|
||||
}
|
||||
//# sourceMappingURL=elementFlags.enum.d.ts.map
|
||||
@@ -0,0 +1,6 @@
|
||||
function _interopRequireDefault(e) {
|
||||
return e && e.__esModule ? e : {
|
||||
"default": e
|
||||
};
|
||||
}
|
||||
module.exports = _interopRequireDefault, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
||||
@@ -0,0 +1,100 @@
|
||||
import type { InternalSymbolName } from "#enums/internalSymbolName";
|
||||
import type { LanguageVariant } from "#enums/languageVariant";
|
||||
import type { NodeFlags } from "#enums/nodeFlags";
|
||||
import type { ScriptKind } from "#enums/scriptKind";
|
||||
import { SyntaxKind } from "#enums/syntaxKind";
|
||||
import type { EndOfFile, EntityName, Identifier, KeywordSyntaxKind, ModifierSyntaxKind, PropertyAccessExpression, PunctuationSyntaxKind, Statement, ThisExpression, Token } from "./ast.generated.ts";
|
||||
export { SyntaxKind } from "#enums/syntaxKind";
|
||||
export { TokenFlags } from "#enums/tokenFlags";
|
||||
export * from "./ast.generated.ts";
|
||||
export type Path = string & {
|
||||
__pathBrand: any;
|
||||
};
|
||||
/**
|
||||
* The escaped form of a symbol/identifier name. Internal compiler names are
|
||||
* prefixed with `__` (e.g. `__type`, `__call`), and user names that already
|
||||
* begin with `__` carry an extra leading underscore. Use
|
||||
* {@link unescapeLeadingUnderscores} to recover the display name and
|
||||
* {@link escapeLeadingUnderscores} to produce a key from a display name.
|
||||
*/
|
||||
export type __String = (string & {
|
||||
__escapedIdentifier: void;
|
||||
}) | InternalSymbolName;
|
||||
export interface TextRange {
|
||||
pos: number;
|
||||
end: number;
|
||||
}
|
||||
export interface ReadonlyTextRange {
|
||||
readonly pos: number;
|
||||
readonly end: number;
|
||||
}
|
||||
export interface NodeArray<T extends Node> extends ReadonlyArray<T>, ReadonlyTextRange {
|
||||
hasTrailingComma?: boolean;
|
||||
transformFlags: number;
|
||||
}
|
||||
export interface Node extends ReadonlyTextRange {
|
||||
readonly kind: SyntaxKind;
|
||||
readonly flags: NodeFlags;
|
||||
readonly parent: Node;
|
||||
readonly jsDoc?: readonly Node[] | undefined;
|
||||
forEachChild<T>(visitor: (node: Node) => T, visitArray?: (nodes: NodeArray<Node>) => T): T | undefined;
|
||||
getSourceFile(): SourceFile;
|
||||
getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number;
|
||||
getFullStart(): number;
|
||||
getEnd(): number;
|
||||
getWidth(sourceFile?: SourceFile): number;
|
||||
getFullWidth(): number;
|
||||
getLeadingTriviaWidth(sourceFile?: SourceFile): number;
|
||||
getFullText(sourceFile?: SourceFile): string;
|
||||
getText(sourceFile?: SourceFile): string;
|
||||
}
|
||||
export interface FileReference extends TextRange {
|
||||
readonly fileName: string;
|
||||
readonly resolutionMode: number;
|
||||
readonly preserve: boolean;
|
||||
}
|
||||
export interface LineAndCharacter {
|
||||
/** 0-based line number. */
|
||||
readonly line: number;
|
||||
/** 0-based character offset, in UTF-16 code units, from the start of the line. */
|
||||
readonly character: number;
|
||||
}
|
||||
export interface SourceFile extends Node {
|
||||
readonly kind: SyntaxKind.SourceFile;
|
||||
readonly statements: NodeArray<Statement>;
|
||||
readonly endOfFileToken: EndOfFile;
|
||||
readonly text: string;
|
||||
readonly fileName: string;
|
||||
readonly path: Path;
|
||||
readonly languageVariant: LanguageVariant;
|
||||
readonly scriptKind: ScriptKind;
|
||||
readonly isDeclarationFile: boolean;
|
||||
readonly referencedFiles: readonly FileReference[];
|
||||
readonly typeReferenceDirectives: readonly FileReference[];
|
||||
readonly libReferenceDirectives: readonly FileReference[];
|
||||
readonly imports: readonly Node[];
|
||||
readonly moduleAugmentations: readonly Node[];
|
||||
readonly ambientModuleNames: readonly string[];
|
||||
readonly externalModuleIndicator: Node | true | undefined;
|
||||
/** Returns the UTF-16 code unit offset of the start of each line. */
|
||||
getLineStarts(): readonly number[];
|
||||
/** Converts a UTF-16 code unit position into a 0-based line and character. */
|
||||
getLineAndCharacterOfPosition(position: number): LineAndCharacter;
|
||||
/** Converts a 0-based line and character into a UTF-16 code unit position. */
|
||||
getPositionOfLineAndCharacter(line: number, character: number): number;
|
||||
/** @internal */
|
||||
tokenCache?: Map<string, Node>;
|
||||
}
|
||||
export type PunctuationToken<TKind extends PunctuationSyntaxKind> = Token<TKind>;
|
||||
export type KeywordToken<TKind extends KeywordSyntaxKind> = Token<TKind>;
|
||||
export type ModifierToken<TKind extends ModifierSyntaxKind> = KeywordToken<TKind>;
|
||||
export interface PropertyAccessEntityNameExpression extends PropertyAccessExpression {
|
||||
readonly expression: EntityNameExpression;
|
||||
readonly name: Identifier;
|
||||
}
|
||||
export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression;
|
||||
export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression;
|
||||
export interface JsxTagNamePropertyAccess extends PropertyAccessExpression {
|
||||
readonly expression: Identifier | ThisExpression | JsxTagNamePropertyAccess;
|
||||
}
|
||||
//# sourceMappingURL=ast.d.ts.map
|
||||
@@ -0,0 +1,147 @@
|
||||
var assert = require('assert');
|
||||
var stringTest = "Aa1 Bb2 Cc3 \u0000\u001F\u0020\uFFFF☃\"\\\/\f\n\r\t\b";
|
||||
var stringResult = JSON.stringify(stringTest);
|
||||
|
||||
var strReg = /[\u0000-\u001f"\\]/g;
|
||||
|
||||
function strReplace(str) {
|
||||
var code = str.charCodeAt(0);
|
||||
switch (code) {
|
||||
case 34: return '\\"';
|
||||
case 92: return '\\\\';
|
||||
case 12: return "\\f";
|
||||
case 10: return "\\n";
|
||||
case 13: return "\\r";
|
||||
case 9: return "\\t";
|
||||
case 8: return "\\b";
|
||||
default:
|
||||
if (code > 15) {
|
||||
return "\\u00" + code.toString(16);
|
||||
} else {
|
||||
return "\\u000" + code.toString(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function strEscapeIf(str){
|
||||
var length = str.length;
|
||||
var buffer = '';
|
||||
var code = 0;
|
||||
var i = 0;
|
||||
|
||||
for (; i < length; i++) {
|
||||
code = str.charCodeAt(i);
|
||||
|
||||
if (code === 34) buffer += '\\"';
|
||||
else if (code === 92) buffer += '\\\\';
|
||||
else if (code > 31) buffer += str[i];
|
||||
else if (code > 15) buffer += "\\u00" + code.toString(16);
|
||||
else if (code === 12) buffer += "\\f";
|
||||
else if (code === 10) buffer += "\\n";
|
||||
else if (code === 13) buffer += "\\r";
|
||||
else if (code === 9) buffer += "\\t";
|
||||
else if (code === 8) buffer += "\\b";
|
||||
else buffer += "\\u000" + code.toString(16);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function strEscapeIfReverse(str){
|
||||
var buffer = '';
|
||||
var code = 0;
|
||||
var i = str.length - 1;
|
||||
|
||||
for (; i >= 0; i--) {
|
||||
code = str.charCodeAt(i);
|
||||
|
||||
if (code === 34) buffer = '\\"' + buffer;
|
||||
else if (code === 92) buffer = '\\\\' + buffer;
|
||||
else if (code > 31) buffer = str[i] + buffer;
|
||||
else if (code > 15) buffer = "\\u00" + code.toString(16) + buffer;
|
||||
else if (code === 12) buffer = "\\f" + buffer;
|
||||
else if (code === 10) buffer = "\\n" + buffer;
|
||||
else if (code === 13) buffer = "\\r" + buffer;
|
||||
else if (code === 9) buffer = "\\t" + buffer;
|
||||
else if (code === 8) buffer = "\\b" + buffer;
|
||||
else buffer = "\\u000" + code.toString(16) + buffer;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
var escape31 = {
|
||||
'31': "\\u001f",
|
||||
'30': "\\u001e",
|
||||
'29': "\\u001d",
|
||||
'28': "\\u001c",
|
||||
'27': "\\u001b",
|
||||
'26': "\\u001a",
|
||||
'25': "\\u0019",
|
||||
'24': "\\u0018",
|
||||
'23': "\\u0017",
|
||||
'22': "\\u0016",
|
||||
'21': "\\u0015",
|
||||
'20': "\\u0014",
|
||||
'19': "\\u0013",
|
||||
'18': "\\u0012",
|
||||
'17': "\\u0011",
|
||||
'16': "\\u0010",
|
||||
'15': "\\u000f",
|
||||
'14': "\\u000e",
|
||||
'13': "\\r",
|
||||
'12': "\\f",
|
||||
'11': "\\u000b",
|
||||
'10': "\\n",
|
||||
'9': "\\t",
|
||||
'8': "\\b",
|
||||
'7': "\\u0007",
|
||||
'6': "\\u0006",
|
||||
'5': "\\u0005",
|
||||
'4': "\\u0004",
|
||||
'3': "\\u0003",
|
||||
'2': "\\u0002",
|
||||
'1': "\\u0001",
|
||||
'0': "\\u0000"
|
||||
};
|
||||
|
||||
function stringEscapeObj(str) {
|
||||
var i = 0;
|
||||
var max = str.length;
|
||||
var buffer = '';
|
||||
var code = 0;
|
||||
for (; i < max; i++) {
|
||||
code = str.charCodeAt(i);
|
||||
|
||||
if (code <= 31) buffer += escape31[code];
|
||||
else if (code === 34) buffer += '\\"';
|
||||
else if (code === 92) buffer += '\\\\';
|
||||
else buffer += str[i];
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
suite("escape-short", function() {
|
||||
|
||||
var minSamples = 120;
|
||||
|
||||
benchmark("reg", function() {
|
||||
assert.equal('"'+stringTest.replace(strReg, strReplace)+'"', stringResult);
|
||||
}, { minSamples: minSamples });
|
||||
|
||||
benchmark("fn if", function() {
|
||||
assert.equal('"'+strEscapeIf(stringTest)+'"', stringResult);
|
||||
}, { minSamples: minSamples });
|
||||
|
||||
benchmark("fn if reverse", function() {
|
||||
assert.equal('"'+strEscapeIfReverse(stringTest)+'"', stringResult);
|
||||
}, { minSamples: minSamples });
|
||||
|
||||
benchmark("escape31", function() {
|
||||
assert.equal('"'+stringEscapeObj(stringTest)+'"', stringResult);
|
||||
}, { minSamples: minSamples });
|
||||
|
||||
benchmark("native", function() {
|
||||
assert.equal(JSON.stringify(stringTest), stringResult);
|
||||
}, { minSamples: minSamples })
|
||||
});
|
||||
@@ -0,0 +1,218 @@
|
||||
import { parseFiles } from "@ast-grep/napi";
|
||||
import { chalk, fs, path } from "zx";
|
||||
import { errors } from "./errors.js";
|
||||
import { root } from "./utils.js";
|
||||
|
||||
/**
|
||||
* @typedef {import("@ast-grep/napi").SgNode} SgNode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prepend text to a node
|
||||
* @param {SgNode} node
|
||||
* @param {string} text
|
||||
* @returns {import("@ast-grep/napi").Edit}
|
||||
*/
|
||||
function prepend(node, text) {
|
||||
const index = node.range().start.index;
|
||||
return {
|
||||
startPos: index,
|
||||
endPos: index,
|
||||
insertedText: text,
|
||||
};
|
||||
}
|
||||
|
||||
export function ast_grep() {
|
||||
const task_queue = [];
|
||||
|
||||
const task = parseFiles([root("esm")], (err, tree) => {
|
||||
const filename = path.basename(tree.filename(), ".js");
|
||||
if (filename === "index") {
|
||||
return;
|
||||
}
|
||||
|
||||
const root_node = tree.root();
|
||||
const edits = [];
|
||||
|
||||
edits.push(prepend(root_node, `"use strict";\n\n`));
|
||||
|
||||
// We have forked _ts_generator from tslib
|
||||
if (filename.startsWith("_ts") && filename !== "_ts_generator") {
|
||||
const match = root_node.find(`export { $NAME as _ } from "tslib"`);
|
||||
if (match) {
|
||||
const name = match.getMatch("NAME").text();
|
||||
edits.push(
|
||||
match.replace(`exports._ = require("tslib").${name};`),
|
||||
);
|
||||
task_queue.push(
|
||||
fs.writeFile(root("cjs", `${filename}.cjs`), root_node.commitEdits(edits), {
|
||||
encoding: "utf-8",
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
report_noexport(tree.filename());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// rewrite export named function
|
||||
const match = root_node.find({
|
||||
rule: {
|
||||
kind: "export_statement",
|
||||
pattern: "export { $FUNC as _ }",
|
||||
},
|
||||
});
|
||||
|
||||
if (match) {
|
||||
const func = match.getMatch("FUNC");
|
||||
const func_name = func.text();
|
||||
if (func_name !== filename) {
|
||||
report_export_mismatch(tree.filename(), match);
|
||||
}
|
||||
|
||||
edits.push(
|
||||
match.replace(`exports._ = ${func_name};`),
|
||||
);
|
||||
|
||||
// since we match the { export x as _ } pattern,
|
||||
// we need to find the assignment expression from the root
|
||||
root_node
|
||||
.findAll({
|
||||
rule: {
|
||||
pattern: func_name,
|
||||
kind: "identifier",
|
||||
inside: { kind: "assignment_expression", field: "left" },
|
||||
},
|
||||
})
|
||||
.forEach((node) => {
|
||||
edits.push(
|
||||
prepend(node, `exports._ = `),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
report_noexport(tree.filename());
|
||||
}
|
||||
|
||||
// rewrite import
|
||||
root_node
|
||||
.findAll({ rule: { pattern: `import { _ as $BINDING } from "$SOURCE"` } })
|
||||
.forEach((match) => {
|
||||
const import_binding = match.getMatch("BINDING").text();
|
||||
const import_source = match.getMatch("SOURCE").text();
|
||||
|
||||
const import_basename = path.basename(import_source, ".js");
|
||||
|
||||
if (import_binding !== import_basename) {
|
||||
report_import_mismatch(tree.filename(), match);
|
||||
}
|
||||
|
||||
edits.push(
|
||||
match.replace(`var ${import_binding} = require("./${import_binding}.cjs");`),
|
||||
);
|
||||
|
||||
root_node
|
||||
.findAll({
|
||||
rule: {
|
||||
pattern: import_binding,
|
||||
kind: "identifier",
|
||||
inside: {
|
||||
not: {
|
||||
kind: "import_specifier",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.forEach((node) => {
|
||||
edits.push(
|
||||
node.replace(`${node.text()}._`),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
task_queue.push(
|
||||
fs.writeFile(root("cjs", `${filename}.cjs`), root_node.commitEdits(edits), {
|
||||
encoding: "utf-8",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
task_queue.push(task);
|
||||
|
||||
return task_queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filename
|
||||
* @param {SgNode} match
|
||||
*/
|
||||
function report_export_mismatch(filename, match) {
|
||||
const func = match.getMatch("FUNC");
|
||||
const func_range = func.range();
|
||||
|
||||
const text = match.text().split("\n");
|
||||
const offset = func_range.start.line - match.range().start.line;
|
||||
|
||||
text.splice(
|
||||
offset + 1,
|
||||
text.length,
|
||||
chalk.red(
|
||||
[
|
||||
" ".repeat(func_range.start.column),
|
||||
"^".repeat(func_range.end.column - func_range.start.column),
|
||||
]
|
||||
.join(""),
|
||||
),
|
||||
);
|
||||
|
||||
errors.push(
|
||||
[
|
||||
`${chalk.bold.red("error")}: mismatch exported function name.`,
|
||||
"",
|
||||
`${chalk.blue("-->")} ${filename}:${func_range.start.line + 1}:${func_range.start.column + 1}`,
|
||||
"",
|
||||
...text,
|
||||
"",
|
||||
`${chalk.bold("note:")} The exported name should be the same as the filename.`,
|
||||
"",
|
||||
]
|
||||
.join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filename
|
||||
* @param {SgNode} match
|
||||
*/
|
||||
function report_import_mismatch(filename, match) {
|
||||
const binding_range = match.getMatch("BINDING").range();
|
||||
const source_range = match.getMatch("SOURCE").range();
|
||||
|
||||
errors.push(
|
||||
[
|
||||
`${chalk.bold.red("error")}: mismatch imported binding name.`,
|
||||
"",
|
||||
`${chalk.blue("-->")} ${filename}:${match.range().start.line + 1}`,
|
||||
"",
|
||||
match.text(),
|
||||
[
|
||||
" ".repeat(binding_range.start.column),
|
||||
chalk.red("^".repeat(binding_range.end.column - binding_range.start.column)),
|
||||
" ".repeat(source_range.start.column - binding_range.end.column),
|
||||
chalk.blue("-".repeat(source_range.end.column - source_range.start.column)),
|
||||
]
|
||||
.join(""),
|
||||
`${chalk.bold("note:")} The imported binding name should be the same as the import source basename.`,
|
||||
"",
|
||||
]
|
||||
.join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filename
|
||||
*/
|
||||
function report_noexport(filename) {
|
||||
errors.push(
|
||||
[`${chalk.bold.red("error")}: exported name not found`, `${chalk.blue("-->")} ${filename}`].join("\n"),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/**
|
||||
* @fileoverview Rule to check empty newline between class members
|
||||
* @author 薛定谔的猫<hh_2013@foxmail.com>
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Types of class members.
|
||||
* Those have `test` method to check it matches to the given class member.
|
||||
* @private
|
||||
*/
|
||||
const ClassMemberTypes = {
|
||||
"*": { test: () => true },
|
||||
field: { test: node => node.type === "PropertyDefinition" },
|
||||
method: { test: node => node.type === "MethodDefinition" },
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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: "lines-between-class-members",
|
||||
url: "https://eslint.style/rules/lines-between-class-members",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Require or disallow an empty line between class members",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/lines-between-class-members",
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
anyOf: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
enforce: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
blankLine: {
|
||||
enum: ["always", "never"],
|
||||
},
|
||||
prev: {
|
||||
enum: ["method", "field", "*"],
|
||||
},
|
||||
next: {
|
||||
enum: ["method", "field", "*"],
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: ["blankLine", "prev", "next"],
|
||||
},
|
||||
minItems: 1,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: ["enforce"],
|
||||
},
|
||||
{
|
||||
enum: ["always", "never"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptAfterSingleLine: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
messages: {
|
||||
never: "Unexpected blank line between class members.",
|
||||
always: "Expected blank line between class members.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = [];
|
||||
|
||||
options[0] = context.options[0] || "always";
|
||||
options[1] = context.options[1] || { exceptAfterSingleLine: false };
|
||||
|
||||
const configureList =
|
||||
typeof options[0] === "object"
|
||||
? options[0].enforce
|
||||
: [{ blankLine: options[0], prev: "*", next: "*" }];
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Gets a pair of tokens that should be used to check lines between two class member nodes.
|
||||
*
|
||||
* In most cases, this returns the very last token of the current node and
|
||||
* the very first token of the next node.
|
||||
* For example:
|
||||
*
|
||||
* class C {
|
||||
* x = 1; // curLast: `;` nextFirst: `in`
|
||||
* in = 2
|
||||
* }
|
||||
*
|
||||
* There is only one exception. If the given node ends with a semicolon, and it looks like
|
||||
* a semicolon-less style's semicolon - one that is not on the same line as the preceding
|
||||
* token, but is on the line where the next class member starts - this returns the preceding
|
||||
* token and the semicolon as boundary tokens.
|
||||
* For example:
|
||||
*
|
||||
* class C {
|
||||
* x = 1 // curLast: `1` nextFirst: `;`
|
||||
* ;in = 2
|
||||
* }
|
||||
* When determining the desired layout of the code, we should treat this semicolon as
|
||||
* a part of the next class member node instead of the one it technically belongs to.
|
||||
* @param {ASTNode} curNode Current class member node.
|
||||
* @param {ASTNode} nextNode Next class member node.
|
||||
* @returns {Token} The actual last token of `node`.
|
||||
* @private
|
||||
*/
|
||||
function getBoundaryTokens(curNode, nextNode) {
|
||||
const lastToken = sourceCode.getLastToken(curNode);
|
||||
const prevToken = sourceCode.getTokenBefore(lastToken);
|
||||
const nextToken = sourceCode.getFirstToken(nextNode); // skip possible lone `;` between nodes
|
||||
|
||||
const isSemicolonLessStyle =
|
||||
astUtils.isSemicolonToken(lastToken) &&
|
||||
!astUtils.isTokenOnSameLine(prevToken, lastToken) &&
|
||||
astUtils.isTokenOnSameLine(lastToken, nextToken);
|
||||
|
||||
return isSemicolonLessStyle
|
||||
? { curLast: prevToken, nextFirst: lastToken }
|
||||
: { curLast: lastToken, nextFirst: nextToken };
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last token among the consecutive tokens that have no exceed max line difference in between, before the first token in the next member.
|
||||
* @param {Token} prevLastToken The last token in the previous member node.
|
||||
* @param {Token} nextFirstToken The first token in the next member node.
|
||||
* @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
|
||||
* @returns {Token} The last token among the consecutive tokens.
|
||||
*/
|
||||
function findLastConsecutiveTokenAfter(
|
||||
prevLastToken,
|
||||
nextFirstToken,
|
||||
maxLine,
|
||||
) {
|
||||
const after = sourceCode.getTokenAfter(prevLastToken, {
|
||||
includeComments: true,
|
||||
});
|
||||
|
||||
if (
|
||||
after !== nextFirstToken &&
|
||||
after.loc.start.line - prevLastToken.loc.end.line <= maxLine
|
||||
) {
|
||||
return findLastConsecutiveTokenAfter(
|
||||
after,
|
||||
nextFirstToken,
|
||||
maxLine,
|
||||
);
|
||||
}
|
||||
return prevLastToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first token among the consecutive tokens that have no exceed max line difference in between, after the last token in the previous member.
|
||||
* @param {Token} nextFirstToken The first token in the next member node.
|
||||
* @param {Token} prevLastToken The last token in the previous member node.
|
||||
* @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
|
||||
* @returns {Token} The first token among the consecutive tokens.
|
||||
*/
|
||||
function findFirstConsecutiveTokenBefore(
|
||||
nextFirstToken,
|
||||
prevLastToken,
|
||||
maxLine,
|
||||
) {
|
||||
const before = sourceCode.getTokenBefore(nextFirstToken, {
|
||||
includeComments: true,
|
||||
});
|
||||
|
||||
if (
|
||||
before !== prevLastToken &&
|
||||
nextFirstToken.loc.start.line - before.loc.end.line <= maxLine
|
||||
) {
|
||||
return findFirstConsecutiveTokenBefore(
|
||||
before,
|
||||
prevLastToken,
|
||||
maxLine,
|
||||
);
|
||||
}
|
||||
return nextFirstToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a token or comment between two tokens.
|
||||
* @param {Token} before The token before.
|
||||
* @param {Token} after The token after.
|
||||
* @returns {boolean} True if there is a token or comment between two tokens.
|
||||
*/
|
||||
function hasTokenOrCommentBetween(before, after) {
|
||||
return (
|
||||
sourceCode.getTokensBetween(before, after, {
|
||||
includeComments: true,
|
||||
}).length !== 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node matches the given type.
|
||||
* @param {ASTNode} node The class member node to check.
|
||||
* @param {string} type The class member type to check.
|
||||
* @returns {boolean} `true` if the class member node matched the type.
|
||||
* @private
|
||||
*/
|
||||
function match(node, type) {
|
||||
return ClassMemberTypes[type].test(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the last matched configuration from the configureList.
|
||||
* @param {ASTNode} prevNode The previous node to match.
|
||||
* @param {ASTNode} nextNode The current node to match.
|
||||
* @returns {string|null} Padding type or `null` if no matches were found.
|
||||
* @private
|
||||
*/
|
||||
function getPaddingType(prevNode, nextNode) {
|
||||
for (let i = configureList.length - 1; i >= 0; --i) {
|
||||
const configure = configureList[i];
|
||||
const matched =
|
||||
match(prevNode, configure.prev) &&
|
||||
match(nextNode, configure.next);
|
||||
|
||||
if (matched) {
|
||||
return configure.blankLine;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
ClassBody(node) {
|
||||
const body = node.body;
|
||||
|
||||
for (let i = 0; i < body.length - 1; i++) {
|
||||
const curFirst = sourceCode.getFirstToken(body[i]);
|
||||
const { curLast, nextFirst } = getBoundaryTokens(
|
||||
body[i],
|
||||
body[i + 1],
|
||||
);
|
||||
const isMulti = !astUtils.isTokenOnSameLine(
|
||||
curFirst,
|
||||
curLast,
|
||||
);
|
||||
const skip = !isMulti && options[1].exceptAfterSingleLine;
|
||||
const beforePadding = findLastConsecutiveTokenAfter(
|
||||
curLast,
|
||||
nextFirst,
|
||||
1,
|
||||
);
|
||||
const afterPadding = findFirstConsecutiveTokenBefore(
|
||||
nextFirst,
|
||||
curLast,
|
||||
1,
|
||||
);
|
||||
const isPadded =
|
||||
afterPadding.loc.start.line -
|
||||
beforePadding.loc.end.line >
|
||||
1;
|
||||
const hasTokenInPadding = hasTokenOrCommentBetween(
|
||||
beforePadding,
|
||||
afterPadding,
|
||||
);
|
||||
const curLineLastToken = findLastConsecutiveTokenAfter(
|
||||
curLast,
|
||||
nextFirst,
|
||||
0,
|
||||
);
|
||||
const paddingType = getPaddingType(body[i], body[i + 1]);
|
||||
|
||||
if (paddingType === "never" && isPadded) {
|
||||
context.report({
|
||||
node: body[i + 1],
|
||||
messageId: "never",
|
||||
|
||||
fix(fixer) {
|
||||
if (hasTokenInPadding) {
|
||||
return null;
|
||||
}
|
||||
return fixer.replaceTextRange(
|
||||
[
|
||||
beforePadding.range[1],
|
||||
afterPadding.range[0],
|
||||
],
|
||||
"\n",
|
||||
);
|
||||
},
|
||||
});
|
||||
} else if (paddingType === "always" && !skip && !isPadded) {
|
||||
context.report({
|
||||
node: body[i + 1],
|
||||
messageId: "always",
|
||||
|
||||
fix(fixer) {
|
||||
if (hasTokenInPadding) {
|
||||
return null;
|
||||
}
|
||||
return fixer.insertTextAfter(
|
||||
curLineLastToken,
|
||||
"\n",
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
var Cache = module.exports = function Cache() {
|
||||
this._cache = {};
|
||||
};
|
||||
|
||||
|
||||
Cache.prototype.put = function Cache_put(key, value) {
|
||||
this._cache[key] = value;
|
||||
};
|
||||
|
||||
|
||||
Cache.prototype.get = function Cache_get(key) {
|
||||
return this._cache[key];
|
||||
};
|
||||
|
||||
|
||||
Cache.prototype.del = function Cache_del(key) {
|
||||
delete this._cache[key];
|
||||
};
|
||||
|
||||
|
||||
Cache.prototype.clear = function Cache_clear() {
|
||||
this._cache = {};
|
||||
};
|
||||
@@ -0,0 +1,508 @@
|
||||
const CharacterCodesSlash = "/".charCodeAt(0);
|
||||
const CharacterCodesBackslash = "\\".charCodeAt(0);
|
||||
const CharacterCodesColon = ":".charCodeAt(0);
|
||||
const CharacterCodesPercent = "%".charCodeAt(0);
|
||||
const CharacterCodes3 = "3".charCodeAt(0);
|
||||
const CharacterCodesa = "a".charCodeAt(0);
|
||||
const CharacterCodesz = "z".charCodeAt(0);
|
||||
const CharacterCodesA = "A".charCodeAt(0);
|
||||
const CharacterCodesZ = "Z".charCodeAt(0);
|
||||
const CharacterCodesDot = ".".charCodeAt(0);
|
||||
const directorySeparator = "/";
|
||||
const altDirectorySeparator = "\\";
|
||||
const urlSchemeSeparator = "://";
|
||||
const backslashRegExp = /\\/g;
|
||||
// check path for these segments: '', '.'. '..'
|
||||
const relativePathSegmentRegExp = /\/\/|(?:^|\/)\.\.?(?:$|\/)/;
|
||||
/**
|
||||
* Determines whether a charCode corresponds to `/` or `\\`.
|
||||
*/
|
||||
function isAnyDirectorySeparator(charCode) {
|
||||
return charCode === CharacterCodesSlash || charCode === CharacterCodesBackslash;
|
||||
}
|
||||
function isVolumeCharacter(charCode) {
|
||||
return (charCode >= CharacterCodesa && charCode <= CharacterCodesz) ||
|
||||
(charCode >= CharacterCodesA && charCode <= CharacterCodesZ);
|
||||
}
|
||||
function getFileUrlVolumeSeparatorEnd(url, start) {
|
||||
const ch0 = url.charCodeAt(start);
|
||||
if (ch0 === CharacterCodesColon)
|
||||
return start + 1;
|
||||
if (ch0 === CharacterCodesPercent && url.charCodeAt(start + 1) === CharacterCodes3) {
|
||||
const ch2 = url.charCodeAt(start + 2);
|
||||
if (ch2 === CharacterCodesa || ch2 === CharacterCodesA)
|
||||
return start + 3;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/**
|
||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
|
||||
*
|
||||
* For example:
|
||||
* ```ts
|
||||
* getRootLength("a") === 0 // ""
|
||||
* getRootLength("/") === 1 // "/"
|
||||
* getRootLength("c:") === 2 // "c:"
|
||||
* getRootLength("c:d") === 0 // ""
|
||||
* getRootLength("c:/") === 3 // "c:/"
|
||||
* getRootLength("c:\\") === 3 // "c:\\"
|
||||
* getRootLength("//server") === 7 // "//server"
|
||||
* getRootLength("//server/share") === 8 // "//server/"
|
||||
* getRootLength("\\\\server") === 7 // "\\\\server"
|
||||
* getRootLength("\\\\server\\share") === 8 // "\\\\server\\"
|
||||
* getRootLength("file:///path") === 8 // "file:///"
|
||||
* getRootLength("file:///c:") === 10 // "file:///c:"
|
||||
* getRootLength("file:///c:d") === 8 // "file:///"
|
||||
* getRootLength("file:///c:/path") === 11 // "file:///c:/"
|
||||
* getRootLength("file://server") === 13 // "file://server"
|
||||
* getRootLength("file://server/path") === 14 // "file://server/"
|
||||
* getRootLength("http://server") === 13 // "http://server"
|
||||
* getRootLength("http://server/path") === 14 // "http://server/"
|
||||
* ```
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function getRootLength(path) {
|
||||
const rootLength = getEncodedRootLength(path);
|
||||
return rootLength < 0 ? ~rootLength : rootLength;
|
||||
}
|
||||
/**
|
||||
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
|
||||
* If the root is part of a URL, the twos-complement of the root length is returned.
|
||||
*/
|
||||
function getEncodedRootLength(path) {
|
||||
if (!path)
|
||||
return 0;
|
||||
const ch0 = path.charCodeAt(0);
|
||||
// POSIX or UNC
|
||||
if (ch0 === CharacterCodesSlash || ch0 === CharacterCodesBackslash) {
|
||||
if (path.charCodeAt(1) !== ch0)
|
||||
return 1; // POSIX: "/" (or non-normalized "\")
|
||||
const p1 = path.indexOf(ch0 === CharacterCodesSlash ? directorySeparator : altDirectorySeparator, 2);
|
||||
if (p1 < 0)
|
||||
return path.length; // UNC: "//server" or "\\server"
|
||||
return p1 + 1; // UNC: "//server/" or "\\server\"
|
||||
}
|
||||
// DOS
|
||||
if (isVolumeCharacter(ch0) && path.charCodeAt(1) === CharacterCodesColon) {
|
||||
const ch2 = path.charCodeAt(2);
|
||||
if (ch2 === CharacterCodesSlash || ch2 === CharacterCodesBackslash)
|
||||
return 3; // DOS: "c:/" or "c:\"
|
||||
if (path.length === 2)
|
||||
return 2; // DOS: "c:" (but not "c:d")
|
||||
}
|
||||
// URL
|
||||
const schemeEnd = path.indexOf(urlSchemeSeparator);
|
||||
if (schemeEnd !== -1) {
|
||||
const authorityStart = schemeEnd + urlSchemeSeparator.length;
|
||||
const authorityEnd = path.indexOf(directorySeparator, authorityStart);
|
||||
if (authorityEnd !== -1) { // URL: "file:///", "file://server/", "file://server/path"
|
||||
// For local "file" URLs, include the leading DOS volume (if present).
|
||||
// Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
|
||||
// special case interpreted as "the machine from which the URL is being interpreted".
|
||||
const scheme = path.slice(0, schemeEnd);
|
||||
const authority = path.slice(authorityStart, authorityEnd);
|
||||
if (scheme === "file" && (authority === "" || authority === "localhost") &&
|
||||
isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
|
||||
const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
|
||||
if (volumeSeparatorEnd !== -1) {
|
||||
if (path.charCodeAt(volumeSeparatorEnd) === CharacterCodesSlash) {
|
||||
// URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
|
||||
return ~(volumeSeparatorEnd + 1);
|
||||
}
|
||||
if (volumeSeparatorEnd === path.length) {
|
||||
// URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
|
||||
// but not "file:///c:d" or "file:///c%3ad"
|
||||
return ~volumeSeparatorEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
|
||||
}
|
||||
return ~path.length; // URL: "file://server", "http://server"
|
||||
}
|
||||
// relative
|
||||
return 0;
|
||||
}
|
||||
export function getPathComponents(path) {
|
||||
return pathComponents(path, getRootLength(path));
|
||||
}
|
||||
function pathComponents(path, rootLength) {
|
||||
const root = path.substring(0, rootLength);
|
||||
const rest = path.substring(rootLength).split("/");
|
||||
if (rest.length && !lastOrUndefined(rest))
|
||||
rest.pop();
|
||||
return [root, ...rest];
|
||||
}
|
||||
function lastOrUndefined(array) {
|
||||
return array.length ? array[array.length - 1] : undefined;
|
||||
}
|
||||
/**
|
||||
* Determines whether a path has a trailing separator (`/` or `\\`).
|
||||
*/
|
||||
export function hasTrailingDirectorySeparator(path) {
|
||||
return path.length > 0 && isAnyDirectorySeparator(path.charCodeAt(path.length - 1));
|
||||
}
|
||||
/**
|
||||
* Removes a trailing directory separator from a path, if it does not already have one.
|
||||
*/
|
||||
export function removeTrailingDirectorySeparator(path) {
|
||||
if (hasTrailingDirectorySeparator(path)) {
|
||||
return path.substr(0, path.length - 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
/**
|
||||
* Adds a trailing directory separator to a path, if it does not already have one.
|
||||
*/
|
||||
export function ensureTrailingDirectorySeparator(path) {
|
||||
if (!hasTrailingDirectorySeparator(path)) {
|
||||
return path + directorySeparator;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
/**
|
||||
* Normalize path separators, converting `\\` into `/`.
|
||||
*/
|
||||
export function normalizeSlashes(path) {
|
||||
return path.includes("\\")
|
||||
? path.replace(backslashRegExp, directorySeparator)
|
||||
: path;
|
||||
}
|
||||
/**
|
||||
* Combines paths. If a path is absolute, it replaces any previous path. Relative paths are not simplified.
|
||||
*/
|
||||
export function combinePaths(path, ...paths) {
|
||||
if (path)
|
||||
path = normalizeSlashes(path);
|
||||
for (let relativePath of paths) {
|
||||
if (!relativePath)
|
||||
continue;
|
||||
relativePath = normalizeSlashes(relativePath);
|
||||
if (!path || getRootLength(relativePath) !== 0) {
|
||||
path = relativePath;
|
||||
}
|
||||
else {
|
||||
path = ensureTrailingDirectorySeparator(path) + relativePath;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
function simpleNormalizePath(path) {
|
||||
// Most paths don't require normalization
|
||||
if (!relativePathSegmentRegExp.test(path)) {
|
||||
return path;
|
||||
}
|
||||
// Some paths only require cleanup of `/./` or leading `./`
|
||||
let simplified = path.replace(/\/\.\//g, "/");
|
||||
if (simplified.startsWith("./")) {
|
||||
simplified = simplified.slice(2);
|
||||
}
|
||||
if (simplified !== path) {
|
||||
path = simplified;
|
||||
if (!relativePathSegmentRegExp.test(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Returns the normalized absolute path, resolving `.` and `..` segments.
|
||||
*/
|
||||
export function getNormalizedAbsolutePath(path, currentDirectory) {
|
||||
let rootLength = getRootLength(path);
|
||||
if (rootLength === 0 && currentDirectory) {
|
||||
path = combinePaths(currentDirectory, path);
|
||||
rootLength = getRootLength(path);
|
||||
}
|
||||
else {
|
||||
// combinePaths normalizes slashes, so not necessary in the other branch
|
||||
path = normalizeSlashes(path);
|
||||
}
|
||||
const simpleNormalized = simpleNormalizePath(path);
|
||||
if (simpleNormalized !== undefined) {
|
||||
return simpleNormalized.length > rootLength ? removeTrailingDirectorySeparator(simpleNormalized) : simpleNormalized;
|
||||
}
|
||||
const length = path.length;
|
||||
const root = path.substring(0, rootLength);
|
||||
// `normalized` is only initialized once `path` is determined to be non-normalized
|
||||
let normalized;
|
||||
let index = rootLength;
|
||||
let segmentStart = index;
|
||||
let normalizedUpTo = index;
|
||||
let seenNonDotDotSegment = rootLength !== 0;
|
||||
while (index < length) {
|
||||
// At beginning of segment
|
||||
segmentStart = index;
|
||||
let ch = path.charCodeAt(index);
|
||||
while (ch === CharacterCodesSlash && index + 1 < length) {
|
||||
index++;
|
||||
ch = path.charCodeAt(index);
|
||||
}
|
||||
if (index > segmentStart) {
|
||||
// Seen superfluous separator
|
||||
normalized ??= path.substring(0, segmentStart - 1);
|
||||
segmentStart = index;
|
||||
}
|
||||
// Past any superfluous separators
|
||||
let segmentEnd = path.indexOf(directorySeparator, index + 1);
|
||||
if (segmentEnd === -1) {
|
||||
segmentEnd = length;
|
||||
}
|
||||
const segmentLength = segmentEnd - segmentStart;
|
||||
if (segmentLength === 1 && path.charCodeAt(index) === CharacterCodesDot) {
|
||||
// "." segment (skip)
|
||||
normalized ??= path.substring(0, normalizedUpTo);
|
||||
}
|
||||
else if (segmentLength === 2 && path.charCodeAt(index) === CharacterCodesDot && path.charCodeAt(index + 1) === CharacterCodesDot) {
|
||||
// ".." segment
|
||||
if (!seenNonDotDotSegment) {
|
||||
if (normalized !== undefined) {
|
||||
normalized += normalized.length === rootLength ? ".." : "/..";
|
||||
}
|
||||
else {
|
||||
normalizedUpTo = index + 2;
|
||||
}
|
||||
}
|
||||
else if (normalized === undefined) {
|
||||
if (normalizedUpTo - 2 >= 0) {
|
||||
normalized = path.substring(0, Math.max(rootLength, path.lastIndexOf(directorySeparator, normalizedUpTo - 2)));
|
||||
}
|
||||
else {
|
||||
normalized = path.substring(0, normalizedUpTo);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const lastSlash = normalized.lastIndexOf(directorySeparator);
|
||||
if (lastSlash !== -1) {
|
||||
normalized = normalized.substring(0, Math.max(rootLength, lastSlash));
|
||||
}
|
||||
else {
|
||||
normalized = root;
|
||||
}
|
||||
if (normalized.length === rootLength) {
|
||||
seenNonDotDotSegment = rootLength !== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (normalized !== undefined) {
|
||||
if (normalized.length !== rootLength) {
|
||||
normalized += directorySeparator;
|
||||
}
|
||||
seenNonDotDotSegment = true;
|
||||
normalized += path.substring(segmentStart, segmentEnd);
|
||||
}
|
||||
else {
|
||||
seenNonDotDotSegment = true;
|
||||
normalizedUpTo = segmentEnd;
|
||||
}
|
||||
index = segmentEnd + 1;
|
||||
}
|
||||
return normalized ?? (length > rootLength ? removeTrailingDirectorySeparator(path) : path);
|
||||
}
|
||||
/**
|
||||
* Normalizes a path, resolving `.` and `..` segments and converting backslashes to forward slashes.
|
||||
*/
|
||||
export function normalizePath(path) {
|
||||
path = normalizeSlashes(path);
|
||||
let normalized = simpleNormalizePath(path);
|
||||
if (normalized !== undefined) {
|
||||
return normalized;
|
||||
}
|
||||
normalized = getNormalizedAbsolutePath(path, "");
|
||||
return normalized && hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalized) : normalized;
|
||||
}
|
||||
/**
|
||||
* Determines whether a path is an absolute disk path (e.g. starts with `/`, or a DOS path
|
||||
* like `c:`, `c:\\` or `c:/`).
|
||||
*/
|
||||
export function isRootedDiskPath(path) {
|
||||
return getEncodedRootLength(path) > 0;
|
||||
}
|
||||
/**
|
||||
* Converts a file name to a normalized path.
|
||||
*
|
||||
* @param fileName The file name to convert
|
||||
* @param basePath The base path to use for relative file names
|
||||
* @param getCanonicalFileName A function to get the canonical file name (e.g., toLowerCase for case-insensitive systems)
|
||||
* @returns The normalized path
|
||||
*/
|
||||
export function toPath(fileName, basePath, getCanonicalFileName) {
|
||||
const nonCanonicalizedPath = isRootedDiskPath(fileName)
|
||||
? normalizePath(fileName)
|
||||
: getNormalizedAbsolutePath(fileName, basePath);
|
||||
return getCanonicalFileName(nonCanonicalizedPath);
|
||||
}
|
||||
/**
|
||||
* Creates a getCanonicalFileName function based on case sensitivity.
|
||||
*/
|
||||
export function createGetCanonicalFileName(useCaseSensitiveFileNames) {
|
||||
return useCaseSensitiveFileNames ? identity : toLowerCase;
|
||||
}
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
function toLowerCase(s) {
|
||||
return s.toLowerCase();
|
||||
}
|
||||
const bundledScheme = "bundled:///";
|
||||
/**
|
||||
* Returns true if the path refers to a bundled library file.
|
||||
*/
|
||||
export function isBundled(path) {
|
||||
return path.startsWith(bundledScheme);
|
||||
}
|
||||
/**
|
||||
* Returns true if the file name represents a dynamic/virtual file
|
||||
* that doesn't exist on disk (e.g., untitled files with paths like "^/untitled/...").
|
||||
*/
|
||||
export function isDynamicFileName(fileName) {
|
||||
return fileName.startsWith("^/");
|
||||
}
|
||||
/**
|
||||
* Splits a Windows volume (e.g., "c:") from the rest of the path.
|
||||
* Returns [volume, rest, ok] where ok is true if a volume was found.
|
||||
*/
|
||||
export function splitVolumePath(path) {
|
||||
if (path.length >= 2 && isVolumeCharacter(path.charCodeAt(0)) && path.charCodeAt(1) === CharacterCodesColon) {
|
||||
return [path.substring(0, 2).toLowerCase(), path.substring(2), true];
|
||||
}
|
||||
return ["", path, false];
|
||||
}
|
||||
// Characters that need extra escaping in URI path segments
|
||||
// https://github.com/microsoft/vscode-uri/blob/edfdccd976efaf4bb8fdeca87e97c47257721729/src/uri.ts#L455
|
||||
const extraEscapeChars = {
|
||||
":": "%3A",
|
||||
"/": "%2F",
|
||||
"?": "%3F",
|
||||
"#": "%23",
|
||||
"[": "%5B",
|
||||
"]": "%5D",
|
||||
"@": "%40",
|
||||
"!": "%21",
|
||||
"$": "%24",
|
||||
"&": "%26",
|
||||
"'": "%27",
|
||||
"(": "%28",
|
||||
")": "%29",
|
||||
"*": "%2A",
|
||||
"+": "%2B",
|
||||
",": "%2C",
|
||||
";": "%3B",
|
||||
"=": "%3D",
|
||||
" ": "%20",
|
||||
};
|
||||
function extraEscape(s) {
|
||||
let result = s;
|
||||
for (const [char, escape] of Object.entries(extraEscapeChars)) {
|
||||
result = result.replaceAll(char, escape);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Converts a file name to a document URI.
|
||||
*
|
||||
* @example
|
||||
* fileNameToDocumentURI("/path/to/file.ts") === "file:///path/to/file.ts"
|
||||
* fileNameToDocumentURI("c:/path/to/file.ts") === "file:///c%3A/path/to/file.ts"
|
||||
* fileNameToDocumentURI("^/untitled/ts-nul-authority/Untitled-1") === "untitled:Untitled-1"
|
||||
* fileNameToDocumentURI("^/vscode-vfs/github/microsoft/typescript-go/file.ts") === "vscode-vfs://github/microsoft/typescript-go/file.ts"
|
||||
*/
|
||||
export function fileNameToDocumentURI(fileName) {
|
||||
// Bundled files are returned as-is
|
||||
if (isBundled(fileName)) {
|
||||
return fileName;
|
||||
}
|
||||
// Dynamic/virtual files (untitled, vscode-vfs, etc.) need special handling
|
||||
if (isDynamicFileName(fileName)) {
|
||||
// Format: ^/scheme/authority/path
|
||||
const withoutPrefix = fileName.substring(2); // Remove "^/"
|
||||
const firstSlash = withoutPrefix.indexOf("/");
|
||||
if (firstSlash === -1) {
|
||||
throw new Error("invalid file name: " + fileName);
|
||||
}
|
||||
const scheme = withoutPrefix.substring(0, firstSlash);
|
||||
const rest = withoutPrefix.substring(firstSlash + 1);
|
||||
const secondSlash = rest.indexOf("/");
|
||||
if (secondSlash === -1) {
|
||||
throw new Error("invalid file name: " + fileName);
|
||||
}
|
||||
const authority = rest.substring(0, secondSlash);
|
||||
const path = rest.substring(secondSlash + 1);
|
||||
// ts-nul-authority is a placeholder for URIs without an authority
|
||||
if (authority === "ts-nul-authority") {
|
||||
return scheme + ":" + path;
|
||||
}
|
||||
return scheme + "://" + authority + "/" + path;
|
||||
}
|
||||
// Regular file path - convert to file:// URI
|
||||
let [volume, rest] = splitVolumePath(fileName);
|
||||
if (volume !== "") {
|
||||
volume = "/" + extraEscape(volume);
|
||||
}
|
||||
// Remove leading // for UNC paths (already handled by file://)
|
||||
if (rest.startsWith("//")) {
|
||||
rest = rest.substring(2);
|
||||
}
|
||||
const parts = rest.split("/");
|
||||
const encodedParts = parts.map(part => extraEscape(encodeURIComponent(part)));
|
||||
return "file://" + volume + encodedParts.join("/");
|
||||
}
|
||||
/**
|
||||
* Converts a document URI to a file name.
|
||||
*
|
||||
* @example
|
||||
* documentURIToFileName("file:///path/to/file.ts") === "/path/to/file.ts"
|
||||
* documentURIToFileName("file:///c%3A/path/to/file.ts") === "c:/path/to/file.ts"
|
||||
* documentURIToFileName("untitled:Untitled-1") === "^/untitled/ts-nul-authority/Untitled-1"
|
||||
* documentURIToFileName("vscode-vfs://github/microsoft/typescript-go/file.ts") === "^/vscode-vfs/github/microsoft/typescript-go/file.ts"
|
||||
*/
|
||||
export function documentURIToFileName(uri) {
|
||||
// Bundled files are returned as-is
|
||||
if (isBundled(uri)) {
|
||||
return uri;
|
||||
}
|
||||
// Handle file:// URIs
|
||||
if (uri.startsWith("file://")) {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = new URL(uri);
|
||||
}
|
||||
catch {
|
||||
throw new Error("invalid file URI: " + uri);
|
||||
}
|
||||
// UNC path: file://server/share/...
|
||||
if (parsed.host !== "") {
|
||||
return "//" + parsed.host + parsed.pathname;
|
||||
}
|
||||
// Local file - fix Windows path by removing leading slash before volume
|
||||
const path = decodeURIComponent(parsed.pathname);
|
||||
if (path.length >= 3 && path.charCodeAt(0) === CharacterCodesSlash) {
|
||||
const [volume, rest, ok] = splitVolumePath(path.substring(1));
|
||||
if (ok) {
|
||||
return volume + rest;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
// Leave all other URIs escaped so we can round-trip them.
|
||||
// Convert to dynamic file name format: ^/scheme/authority/path
|
||||
const colonIndex = uri.indexOf(":");
|
||||
if (colonIndex === -1) {
|
||||
throw new Error("invalid URI: " + uri);
|
||||
}
|
||||
const scheme = uri.substring(0, colonIndex);
|
||||
let path = uri.substring(colonIndex + 1);
|
||||
let authority = "ts-nul-authority";
|
||||
if (path.startsWith("//")) {
|
||||
const rest = path.substring(2);
|
||||
const slashIndex = rest.indexOf("/");
|
||||
if (slashIndex === -1) {
|
||||
throw new Error("invalid URI: " + uri);
|
||||
}
|
||||
authority = rest.substring(0, slashIndex);
|
||||
path = rest.substring(slashIndex + 1);
|
||||
}
|
||||
return "^/" + scheme + "/" + authority + "/" + path;
|
||||
}
|
||||
//# sourceMappingURL=path.js.map
|
||||
@@ -0,0 +1,6 @@
|
||||
function _arrayLikeToArray(r, a) {
|
||||
(null == a || a > r.length) && (a = r.length);
|
||||
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
|
||||
return n;
|
||||
}
|
||||
module.exports = _arrayLikeToArray, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @fileoverview Define the cursor which iterates tokens only in reverse.
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const Cursor = require("./cursor");
|
||||
const { getLastIndex, getFirstIndex } = require("./utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Exports
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The cursor which iterates tokens only in reverse.
|
||||
*/
|
||||
module.exports = class BackwardTokenCursor extends Cursor {
|
||||
/**
|
||||
* Initializes this cursor.
|
||||
* @param {Token[]} tokens The array of tokens.
|
||||
* @param {Comment[]} comments The array of comments.
|
||||
* @param {Object} indexMap The map from locations to indices in `tokens`.
|
||||
* @param {number} startLoc The start location of the iteration range.
|
||||
* @param {number} endLoc The end location of the iteration range.
|
||||
*/
|
||||
constructor(tokens, comments, indexMap, startLoc, endLoc) {
|
||||
super();
|
||||
this.tokens = tokens;
|
||||
this.index = getLastIndex(tokens, indexMap, endLoc);
|
||||
this.indexEnd = getFirstIndex(tokens, indexMap, startLoc);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
moveNext() {
|
||||
if (this.index >= this.indexEnd) {
|
||||
this.current = this.tokens[this.index];
|
||||
this.index -= 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Shorthand for performance.
|
||||
*
|
||||
*/
|
||||
|
||||
/** @inheritdoc */
|
||||
getOneToken() {
|
||||
return this.index >= this.indexEnd ? this.tokens[this.index] : null;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
const packageJsonPath = path.resolve(import.meta.dirname, '../package.json')
|
||||
let { version } = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
|
||||
|
||||
let passedVersion = process.argv[2]
|
||||
|
||||
if (passedVersion) {
|
||||
passedVersion = passedVersion.trim().replace(/^v/, '')
|
||||
if (version !== passedVersion) {
|
||||
console.log(`Syncing version from ${version} to ${passedVersion}`)
|
||||
version = passedVersion
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
|
||||
packageJson.version = version
|
||||
fs.writeFileSync(path.resolve('./package.json'), JSON.stringify(packageJson, null, 2) + '\n', { encoding: 'utf-8' })
|
||||
}
|
||||
} else {
|
||||
throw new Error('Version argument is required')
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import type { $ZodStringFormats } from "../core/checks.js";
|
||||
import type * as errors from "../core/errors.js";
|
||||
import * as util from "../core/util.js";
|
||||
|
||||
const error: () => errors.$ZodErrorMap = () => {
|
||||
const Sizable: Record<string, { unit: string; verb: string }> = {
|
||||
string: { unit: "எழுத்துக்கள்", verb: "கொண்டிருக்க வேண்டும்" },
|
||||
file: { unit: "பைட்டுகள்", verb: "கொண்டிருக்க வேண்டும்" },
|
||||
array: { unit: "உறுப்புகள்", verb: "கொண்டிருக்க வேண்டும்" },
|
||||
set: { unit: "உறுப்புகள்", verb: "கொண்டிருக்க வேண்டும்" },
|
||||
};
|
||||
|
||||
function getSizing(origin: string): { unit: string; verb: string } | null {
|
||||
return Sizable[origin] ?? null;
|
||||
}
|
||||
|
||||
const FormatDictionary: {
|
||||
[k in $ZodStringFormats | (string & {})]?: string;
|
||||
} = {
|
||||
regex: "உள்ளீடு",
|
||||
email: "மின்னஞ்சல் முகவரி",
|
||||
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: "ISO தேதி நேரம்",
|
||||
date: "ISO தேதி",
|
||||
time: "ISO நேரம்",
|
||||
duration: "ISO கால அளவு",
|
||||
ipv4: "IPv4 முகவரி",
|
||||
ipv6: "IPv6 முகவரி",
|
||||
cidrv4: "IPv4 வரம்பு",
|
||||
cidrv6: "IPv6 வரம்பு",
|
||||
base64: "base64-encoded சரம்",
|
||||
base64url: "base64url-encoded சரம்",
|
||||
json_string: "JSON சரம்",
|
||||
e164: "E.164 எண்",
|
||||
jwt: "JWT",
|
||||
template_literal: "input",
|
||||
};
|
||||
|
||||
const TypeDictionary: {
|
||||
[k in errors.$ZodInvalidTypeExpected | (string & {})]?: string;
|
||||
} = {
|
||||
nan: "NaN",
|
||||
number: "எண்",
|
||||
array: "அணி",
|
||||
null: "வெறுமை",
|
||||
};
|
||||
|
||||
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 `தவறான உள்ளீடு: எதிர்பார்க்கப்பட்டது instanceof ${issue.expected}, பெறப்பட்டது ${received}`;
|
||||
}
|
||||
return `தவறான உள்ளீடு: எதிர்பார்க்கப்பட்டது ${expected}, பெறப்பட்டது ${received}`;
|
||||
}
|
||||
case "invalid_value":
|
||||
if (issue.values.length === 1) return `தவறான உள்ளீடு: எதிர்பார்க்கப்பட்டது ${util.stringifyPrimitive(issue.values[0])}`;
|
||||
return `தவறான விருப்பம்: எதிர்பார்க்கப்பட்டது ${util.joinValues(issue.values, "|")} இல் ஒன்று`;
|
||||
case "too_big": {
|
||||
const adj = issue.inclusive ? "<=" : "<";
|
||||
const sizing = getSizing(issue.origin);
|
||||
if (sizing) {
|
||||
return `மிக பெரியது: எதிர்பார்க்கப்பட்டது ${issue.origin ?? "மதிப்பு"} ${adj}${issue.maximum.toString()} ${sizing.unit ?? "உறுப்புகள்"} ஆக இருக்க வேண்டும்`;
|
||||
}
|
||||
return `மிக பெரியது: எதிர்பார்க்கப்பட்டது ${issue.origin ?? "மதிப்பு"} ${adj}${issue.maximum.toString()} ஆக இருக்க வேண்டும்`;
|
||||
}
|
||||
case "too_small": {
|
||||
const adj = issue.inclusive ? ">=" : ">";
|
||||
const sizing = getSizing(issue.origin);
|
||||
if (sizing) {
|
||||
return `மிகச் சிறியது: எதிர்பார்க்கப்பட்டது ${issue.origin} ${adj}${issue.minimum.toString()} ${sizing.unit} ஆக இருக்க வேண்டும்`; //
|
||||
}
|
||||
|
||||
return `மிகச் சிறியது: எதிர்பார்க்கப்பட்டது ${issue.origin} ${adj}${issue.minimum.toString()} ஆக இருக்க வேண்டும்`;
|
||||
}
|
||||
case "invalid_format": {
|
||||
const _issue = issue as errors.$ZodStringFormatIssues;
|
||||
if (_issue.format === "starts_with") return `தவறான சரம்: "${_issue.prefix}" இல் தொடங்க வேண்டும்`;
|
||||
if (_issue.format === "ends_with") return `தவறான சரம்: "${_issue.suffix}" இல் முடிவடைய வேண்டும்`;
|
||||
if (_issue.format === "includes") return `தவறான சரம்: "${_issue.includes}" ஐ உள்ளடக்க வேண்டும்`;
|
||||
if (_issue.format === "regex") return `தவறான சரம்: ${_issue.pattern} முறைபாட்டுடன் பொருந்த வேண்டும்`;
|
||||
return `தவறான ${FormatDictionary[_issue.format] ?? issue.format}`;
|
||||
}
|
||||
case "not_multiple_of":
|
||||
return `தவறான எண்: ${issue.divisor} இன் பலமாக இருக்க வேண்டும்`;
|
||||
case "unrecognized_keys":
|
||||
return `அடையாளம் தெரியாத விசை${issue.keys.length > 1 ? "கள்" : ""}: ${util.joinValues(issue.keys, ", ")}`;
|
||||
case "invalid_key":
|
||||
return `${issue.origin} இல் தவறான விசை`;
|
||||
case "invalid_union":
|
||||
return "தவறான உள்ளீடு";
|
||||
case "invalid_element":
|
||||
return `${issue.origin} இல் தவறான மதிப்பு`;
|
||||
default:
|
||||
return `தவறான உள்ளீடு`;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default function (): { localeError: errors.$ZodErrorMap } {
|
||||
return {
|
||||
localeError: error(),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user