update
This commit is contained in:
@@ -0,0 +1 @@
|
||||
module.exports={A:{A:{"2":"K D E F A B mC"},B:{"2":"0 9 C L M G N O P Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I"},C:{"2":"0 1 2 3 4 5 6 7 8 9 nC LC J PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB MC wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC oC pC qC rC"},D:{"2":"0 9 lB mB nB oB pB qB rB sB tB uB vB MC wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC","33":"1 2 3 4 5 6 7 8 J PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB"},E:{"2":"sC SC","33":"J PB K D E F A B C L M G tC uC vC wC TC FC GC xC yC zC UC VC HC 0C IC WC XC YC ZC aC 1C JC bC cC dC eC fC 2C KC gC hC iC jC 3C"},F:{"2":"0 F B C YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB wB xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 4C 5C 6C 7C FC kC 8C GC","33":"1 2 3 4 5 6 7 8 G N O P QB RB SB TB UB VB WB XB"},G:{"33":"E SC 9C lC AD BD CD DD ED FD GD HD ID JD KD LD MD ND OD PD QD RD SD UC VC HC TD IC WC XC YC ZC aC UD JC bC cC dC eC fC VD KC gC hC iC jC"},H:{"2":"WD"},I:{"2":"I","33":"LC J XD YD ZD aD lC bD cD"},J:{"33":"D A"},K:{"2":"A B C H FC kC GC"},L:{"2":"I"},M:{"2":"EC"},N:{"2":"A B"},O:{"2":"HC"},P:{"2":"1 2 3 4 5 6 7 8 dD eD fD gD hD TC iD jD kD lD mD IC JC KC nD","33":"J"},Q:{"2":"oD"},R:{"2":"pD"},S:{"2":"qD rD"}},B:7,C:"CSS Canvas Drawings",D:true};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "browserslist",
|
||||
"version": "4.24.4",
|
||||
"description": "Share target browsers between different front-end tools, like Autoprefixer, Stylelint and babel-env-preset",
|
||||
"keywords": [
|
||||
"caniuse",
|
||||
"browsers",
|
||||
"target"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"author": "Andrey Sitnik <andrey@sitnik.ru>",
|
||||
"license": "MIT",
|
||||
"repository": "browserslist/browserslist",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001688",
|
||||
"electron-to-chromium": "^1.5.73",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
},
|
||||
"types": "./index.d.ts",
|
||||
"browser": {
|
||||
"./node.js": "./browser.js",
|
||||
"path": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
export type PDFPageProxy = import("../src/display/api").PDFPageProxy;
|
||||
export type PageViewport = import("../src/display/display_utils").PageViewport;
|
||||
export type AnnotationStorage = import("../src/display/annotation_storage").AnnotationStorage;
|
||||
export type IDownloadManager = import("./interfaces").IDownloadManager;
|
||||
export type IPDFLinkService = import("./interfaces").IPDFLinkService;
|
||||
export type TextAccessibilityManager = import("./text_accessibility.js").TextAccessibilityManager;
|
||||
export type AnnotationEditorUIManager = import("../src/display/editor/tools.js").AnnotationEditorUIManager;
|
||||
export type AnnotationLayerBuilderOptions = {
|
||||
pdfPage: PDFPageProxy;
|
||||
annotationStorage?: import("../src/display/annotation_storage").AnnotationStorage | undefined;
|
||||
/**
|
||||
* - Path for image resources, mainly
|
||||
* for annotation icons. Include trailing slash.
|
||||
*/
|
||||
imageResourcesPath?: string | undefined;
|
||||
renderForms: boolean;
|
||||
linkService: IPDFLinkService;
|
||||
downloadManager?: import("./interfaces").IDownloadManager | undefined;
|
||||
enableScripting?: boolean | undefined;
|
||||
hasJSActionsPromise?: Promise<boolean> | undefined;
|
||||
fieldObjectsPromise?: Promise<{
|
||||
[x: string]: Object[];
|
||||
} | null> | undefined;
|
||||
annotationCanvasMap?: Map<string, HTMLCanvasElement> | undefined;
|
||||
accessibilityManager?: import("./text_accessibility.js").TextAccessibilityManager | undefined;
|
||||
annotationEditorUIManager?: import("../src/pdf").AnnotationEditorUIManager | undefined;
|
||||
onAppend?: Function | undefined;
|
||||
};
|
||||
/**
|
||||
* @typedef {Object} AnnotationLayerBuilderOptions
|
||||
* @property {PDFPageProxy} pdfPage
|
||||
* @property {AnnotationStorage} [annotationStorage]
|
||||
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
||||
* for annotation icons. Include trailing slash.
|
||||
* @property {boolean} renderForms
|
||||
* @property {IPDFLinkService} linkService
|
||||
* @property {IDownloadManager} [downloadManager]
|
||||
* @property {boolean} [enableScripting]
|
||||
* @property {Promise<boolean>} [hasJSActionsPromise]
|
||||
* @property {Promise<Object<string, Array<Object>> | null>}
|
||||
* [fieldObjectsPromise]
|
||||
* @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap]
|
||||
* @property {TextAccessibilityManager} [accessibilityManager]
|
||||
* @property {AnnotationEditorUIManager} [annotationEditorUIManager]
|
||||
* @property {function} [onAppend]
|
||||
*/
|
||||
export class AnnotationLayerBuilder {
|
||||
/**
|
||||
* @param {AnnotationLayerBuilderOptions} options
|
||||
*/
|
||||
constructor({ pdfPage, linkService, downloadManager, annotationStorage, imageResourcesPath, renderForms, enableScripting, hasJSActionsPromise, fieldObjectsPromise, annotationCanvasMap, accessibilityManager, annotationEditorUIManager, onAppend, }: AnnotationLayerBuilderOptions);
|
||||
pdfPage: import("../src/display/api").PDFPageProxy;
|
||||
linkService: import("./interfaces").IPDFLinkService;
|
||||
downloadManager: import("./interfaces").IDownloadManager | undefined;
|
||||
imageResourcesPath: string;
|
||||
renderForms: boolean;
|
||||
annotationStorage: import("../src/display/annotation_storage").AnnotationStorage;
|
||||
enableScripting: boolean;
|
||||
_hasJSActionsPromise: Promise<boolean>;
|
||||
_fieldObjectsPromise: Promise<{
|
||||
[x: string]: Object[];
|
||||
} | null>;
|
||||
_annotationCanvasMap: Map<string, HTMLCanvasElement>;
|
||||
_accessibilityManager: import("./text_accessibility.js").TextAccessibilityManager;
|
||||
_annotationEditorUIManager: import("../src/pdf").AnnotationEditorUIManager;
|
||||
annotationLayer: AnnotationLayer | null;
|
||||
div: HTMLDivElement | null;
|
||||
_cancelled: boolean;
|
||||
_eventBus: any;
|
||||
/**
|
||||
* @param {PageViewport} viewport
|
||||
* @param {Object} options
|
||||
* @param {string} intent (default value is 'display')
|
||||
* @returns {Promise<void>} A promise that is resolved when rendering of the
|
||||
* annotations is complete.
|
||||
*/
|
||||
render(viewport: PageViewport, options: Object, intent?: string): Promise<void>;
|
||||
cancel(): void;
|
||||
hide(): void;
|
||||
hasEditableAnnotations(): boolean;
|
||||
#private;
|
||||
}
|
||||
import { AnnotationLayer } from "../src/pdf";
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @typedef {import("./Resolver")} Resolver */
|
||||
/** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
|
||||
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
|
||||
/** @typedef {import("./Resolver").ResolveContextYield} ResolveContextYield */
|
||||
/** @typedef {{[k: string]: ResolveRequest | ResolveRequest[] | undefined}} Cache */
|
||||
|
||||
/**
|
||||
* @param {string} type type of cache
|
||||
* @param {ResolveRequest} request request
|
||||
* @param {boolean} withContext cache with context?
|
||||
* @returns {string} cache id
|
||||
*/
|
||||
function getCacheId(type, request, withContext) {
|
||||
return JSON.stringify({
|
||||
type,
|
||||
context: withContext ? request.context : "",
|
||||
path: request.path,
|
||||
query: request.query,
|
||||
fragment: request.fragment,
|
||||
request: request.request
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = class UnsafeCachePlugin {
|
||||
/**
|
||||
* @param {string | ResolveStepHook} source source
|
||||
* @param {function(ResolveRequest): boolean} filterPredicate filterPredicate
|
||||
* @param {Cache} cache cache
|
||||
* @param {boolean} withContext withContext
|
||||
* @param {string | ResolveStepHook} target target
|
||||
*/
|
||||
constructor(source, filterPredicate, cache, withContext, target) {
|
||||
this.source = source;
|
||||
this.filterPredicate = filterPredicate;
|
||||
this.withContext = withContext;
|
||||
this.cache = cache;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Resolver} resolver the resolver
|
||||
* @returns {void}
|
||||
*/
|
||||
apply(resolver) {
|
||||
const target = resolver.ensureHook(this.target);
|
||||
resolver
|
||||
.getHook(this.source)
|
||||
.tapAsync("UnsafeCachePlugin", (request, resolveContext, callback) => {
|
||||
if (!this.filterPredicate(request)) return callback();
|
||||
const isYield = typeof resolveContext.yield === "function";
|
||||
const cacheId = getCacheId(
|
||||
isYield ? "yield" : "default",
|
||||
request,
|
||||
this.withContext
|
||||
);
|
||||
const cacheEntry = this.cache[cacheId];
|
||||
if (cacheEntry) {
|
||||
if (isYield) {
|
||||
const yield_ = /** @type {Function} */ (resolveContext.yield);
|
||||
if (Array.isArray(cacheEntry)) {
|
||||
for (const result of cacheEntry) yield_(result);
|
||||
} else {
|
||||
yield_(cacheEntry);
|
||||
}
|
||||
return callback(null, null);
|
||||
}
|
||||
return callback(null, /** @type {ResolveRequest} */ (cacheEntry));
|
||||
}
|
||||
|
||||
/** @type {ResolveContextYield|undefined} */
|
||||
let yieldFn;
|
||||
/** @type {ResolveContextYield|undefined} */
|
||||
let yield_;
|
||||
/** @type {ResolveRequest[]} */
|
||||
const yieldResult = [];
|
||||
if (isYield) {
|
||||
yieldFn = resolveContext.yield;
|
||||
yield_ = result => {
|
||||
yieldResult.push(result);
|
||||
};
|
||||
}
|
||||
|
||||
resolver.doResolve(
|
||||
target,
|
||||
request,
|
||||
null,
|
||||
yield_ ? { ...resolveContext, yield: yield_ } : resolveContext,
|
||||
(err, result) => {
|
||||
if (err) return callback(err);
|
||||
if (isYield) {
|
||||
if (result) yieldResult.push(result);
|
||||
for (const result of yieldResult) {
|
||||
/** @type {ResolveContextYield} */
|
||||
(yieldFn)(result);
|
||||
}
|
||||
this.cache[cacheId] = yieldResult;
|
||||
return callback(null, null);
|
||||
}
|
||||
if (result) return callback(null, (this.cache[cacheId] = result));
|
||||
callback();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag fall-through cases in switch statements.
|
||||
* @author Matt DuVall <http://mattduvall.com/>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const { directivesPattern } = require("../shared/directives");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/iu;
|
||||
|
||||
/**
|
||||
* Checks all segments in a set and returns true if any are reachable.
|
||||
* @param {Set<CodePathSegment>} segments The segments to check.
|
||||
* @returns {boolean} True if any segment is reachable; false otherwise.
|
||||
*/
|
||||
function isAnySegmentReachable(segments) {
|
||||
for (const segment of segments) {
|
||||
if (segment.reachable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given comment string is really a fallthrough comment and not an ESLint directive.
|
||||
* @param {string} comment The comment string to check.
|
||||
* @param {RegExp} fallthroughCommentPattern The regular expression used for checking for fallthrough comments.
|
||||
* @returns {boolean} `true` if the comment string is truly a fallthrough comment.
|
||||
*/
|
||||
function isFallThroughComment(comment, fallthroughCommentPattern) {
|
||||
return (
|
||||
fallthroughCommentPattern.test(comment) &&
|
||||
!directivesPattern.test(comment.trim())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given case has a fallthrough comment.
|
||||
* @param {ASTNode} caseWhichFallsThrough SwitchCase node which falls through.
|
||||
* @param {ASTNode} subsequentCase The case after caseWhichFallsThrough.
|
||||
* @param {RuleContext} context A rule context which stores comments.
|
||||
* @param {RegExp} fallthroughCommentPattern A pattern to match comment to.
|
||||
* @returns {null | object} the comment if the case has a valid fallthrough comment, otherwise null
|
||||
*/
|
||||
function getFallthroughComment(
|
||||
caseWhichFallsThrough,
|
||||
subsequentCase,
|
||||
context,
|
||||
fallthroughCommentPattern,
|
||||
) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
if (
|
||||
caseWhichFallsThrough.consequent.length === 1 &&
|
||||
caseWhichFallsThrough.consequent[0].type === "BlockStatement"
|
||||
) {
|
||||
const trailingCloseBrace = sourceCode.getLastToken(
|
||||
caseWhichFallsThrough.consequent[0],
|
||||
);
|
||||
const commentInBlock = sourceCode
|
||||
.getCommentsBefore(trailingCloseBrace)
|
||||
.pop();
|
||||
|
||||
if (
|
||||
commentInBlock &&
|
||||
isFallThroughComment(
|
||||
commentInBlock.value,
|
||||
fallthroughCommentPattern,
|
||||
)
|
||||
) {
|
||||
return commentInBlock;
|
||||
}
|
||||
}
|
||||
|
||||
const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
|
||||
|
||||
if (
|
||||
comment &&
|
||||
isFallThroughComment(comment.value, fallthroughCommentPattern)
|
||||
) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a node and a token are separated by blank lines
|
||||
* @param {ASTNode} node The node to check
|
||||
* @param {Token} token The token to compare against
|
||||
* @returns {boolean} `true` if there are blank lines between node and token
|
||||
*/
|
||||
function hasBlankLinesBetween(node, token) {
|
||||
return token.loc.start.line > node.loc.end.line + 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
defaultOptions: [
|
||||
{
|
||||
allowEmptyCase: false,
|
||||
reportUnusedFallthroughComment: false,
|
||||
},
|
||||
],
|
||||
|
||||
docs: {
|
||||
description: "Disallow fallthrough of `case` statements",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-fallthrough",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
commentPattern: {
|
||||
type: "string",
|
||||
},
|
||||
allowEmptyCase: {
|
||||
type: "boolean",
|
||||
},
|
||||
reportUnusedFallthroughComment: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
messages: {
|
||||
unusedFallthroughComment:
|
||||
"Found a comment that would permit fallthrough, but case cannot fall through.",
|
||||
case: "Expected a 'break' statement before 'case'.",
|
||||
default: "Expected a 'break' statement before 'default'.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const codePathSegments = [];
|
||||
let currentCodePathSegments = new Set();
|
||||
const sourceCode = context.sourceCode;
|
||||
const [
|
||||
{ allowEmptyCase, commentPattern, reportUnusedFallthroughComment },
|
||||
] = context.options;
|
||||
const fallthroughCommentPattern = commentPattern
|
||||
? new RegExp(commentPattern, "u")
|
||||
: DEFAULT_FALLTHROUGH_COMMENT;
|
||||
|
||||
/*
|
||||
* We need to use leading comments of the next SwitchCase node because
|
||||
* trailing comments is wrong if semicolons are omitted.
|
||||
*/
|
||||
let previousCase = null;
|
||||
|
||||
return {
|
||||
onCodePathStart() {
|
||||
codePathSegments.push(currentCodePathSegments);
|
||||
currentCodePathSegments = new Set();
|
||||
},
|
||||
|
||||
onCodePathEnd() {
|
||||
currentCodePathSegments = codePathSegments.pop();
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentStart(segment) {
|
||||
currentCodePathSegments.add(segment);
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentEnd(segment) {
|
||||
currentCodePathSegments.delete(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentStart(segment) {
|
||||
currentCodePathSegments.add(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentEnd(segment) {
|
||||
currentCodePathSegments.delete(segment);
|
||||
},
|
||||
|
||||
SwitchCase(node) {
|
||||
/*
|
||||
* Checks whether or not there is a fallthrough comment.
|
||||
* And reports the previous fallthrough node if that does not exist.
|
||||
*/
|
||||
|
||||
if (previousCase && previousCase.node.parent === node.parent) {
|
||||
const previousCaseFallthroughComment =
|
||||
getFallthroughComment(
|
||||
previousCase.node,
|
||||
node,
|
||||
context,
|
||||
fallthroughCommentPattern,
|
||||
);
|
||||
|
||||
if (
|
||||
previousCase.isFallthrough &&
|
||||
!previousCaseFallthroughComment
|
||||
) {
|
||||
context.report({
|
||||
messageId: node.test ? "case" : "default",
|
||||
node,
|
||||
});
|
||||
} else if (
|
||||
reportUnusedFallthroughComment &&
|
||||
!previousCase.isSwitchExitReachable &&
|
||||
previousCaseFallthroughComment
|
||||
) {
|
||||
context.report({
|
||||
messageId: "unusedFallthroughComment",
|
||||
node: previousCaseFallthroughComment,
|
||||
});
|
||||
}
|
||||
}
|
||||
previousCase = null;
|
||||
},
|
||||
|
||||
"SwitchCase:exit"(node) {
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
|
||||
/*
|
||||
* `reachable` meant fall through because statements preceded by
|
||||
* `break`, `return`, or `throw` are unreachable.
|
||||
* And allows empty cases and the last case.
|
||||
*/
|
||||
const isSwitchExitReachable = isAnySegmentReachable(
|
||||
currentCodePathSegments,
|
||||
);
|
||||
const isFallthrough =
|
||||
isSwitchExitReachable &&
|
||||
(node.consequent.length > 0 ||
|
||||
(!allowEmptyCase &&
|
||||
hasBlankLinesBetween(node, nextToken))) &&
|
||||
node.parent.cases.at(-1) !== node;
|
||||
|
||||
previousCase = {
|
||||
node,
|
||||
isSwitchExitReachable,
|
||||
isFallthrough,
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,642 @@
|
||||
// While the public API was clearly inspired by the "history" npm package,
|
||||
// This implementation attempts to be more lightweight by
|
||||
// making assumptions about the way TanStack Router works
|
||||
|
||||
export interface NavigateOptions {
|
||||
ignoreBlocker?: boolean
|
||||
}
|
||||
|
||||
type SubscriberHistoryAction =
|
||||
| {
|
||||
type: Exclude<HistoryAction, 'GO'>
|
||||
}
|
||||
| {
|
||||
type: 'GO'
|
||||
index: number
|
||||
}
|
||||
|
||||
type SubscriberArgs = {
|
||||
location: HistoryLocation
|
||||
action: SubscriberHistoryAction
|
||||
}
|
||||
|
||||
export interface RouterHistory {
|
||||
location: HistoryLocation
|
||||
length: number
|
||||
subscribers: Set<(opts: SubscriberArgs) => void>
|
||||
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void
|
||||
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void
|
||||
replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void
|
||||
go: (index: number, navigateOpts?: NavigateOptions) => void
|
||||
back: (navigateOpts?: NavigateOptions) => void
|
||||
forward: (navigateOpts?: NavigateOptions) => void
|
||||
canGoBack: () => boolean
|
||||
createHref: (href: string) => string
|
||||
block: (blocker: NavigationBlocker) => () => void
|
||||
flush: () => void
|
||||
destroy: () => void
|
||||
notify: (action: SubscriberHistoryAction) => void
|
||||
_ignoreSubscribers?: boolean
|
||||
}
|
||||
|
||||
export interface HistoryLocation extends ParsedPath {
|
||||
state: ParsedHistoryState
|
||||
}
|
||||
|
||||
export interface ParsedPath {
|
||||
href: string
|
||||
pathname: string
|
||||
search: string
|
||||
hash: string
|
||||
}
|
||||
|
||||
export interface HistoryState {}
|
||||
|
||||
export type ParsedHistoryState = HistoryState & {
|
||||
key?: string
|
||||
__TSR_index: number
|
||||
}
|
||||
|
||||
type ShouldAllowNavigation = any
|
||||
|
||||
export type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'
|
||||
|
||||
export type BlockerFnArgs = {
|
||||
currentLocation: HistoryLocation
|
||||
nextLocation: HistoryLocation
|
||||
action: HistoryAction
|
||||
}
|
||||
|
||||
export type BlockerFn = (
|
||||
args: BlockerFnArgs,
|
||||
) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation
|
||||
|
||||
export type NavigationBlocker = {
|
||||
blockerFn: BlockerFn
|
||||
enableBeforeUnload?: (() => boolean) | boolean
|
||||
}
|
||||
|
||||
type TryNavigateArgs = {
|
||||
task: () => void
|
||||
type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'
|
||||
navigateOpts?: NavigateOptions
|
||||
} & (
|
||||
| {
|
||||
type: 'PUSH' | 'REPLACE'
|
||||
path: string
|
||||
state: any
|
||||
}
|
||||
| {
|
||||
type: 'BACK' | 'FORWARD' | 'GO'
|
||||
}
|
||||
)
|
||||
|
||||
const stateIndexKey = '__TSR_index'
|
||||
const popStateEvent = 'popstate'
|
||||
const beforeUnloadEvent = 'beforeunload'
|
||||
|
||||
export function createHistory(opts: {
|
||||
getLocation: () => HistoryLocation
|
||||
getLength: () => number
|
||||
pushState: (path: string, state: any) => void
|
||||
replaceState: (path: string, state: any) => void
|
||||
go: (n: number) => void
|
||||
back: (ignoreBlocker: boolean) => void
|
||||
forward: (ignoreBlocker: boolean) => void
|
||||
createHref: (path: string) => string
|
||||
flush?: () => void
|
||||
destroy?: () => void
|
||||
onBlocked?: () => void
|
||||
getBlockers?: () => Array<NavigationBlocker>
|
||||
setBlockers?: (blockers: Array<NavigationBlocker>) => void
|
||||
// Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event
|
||||
notifyOnIndexChange?: boolean
|
||||
}): RouterHistory {
|
||||
let location = opts.getLocation()
|
||||
const subscribers = new Set<(opts: SubscriberArgs) => void>()
|
||||
|
||||
const notify = (action: SubscriberHistoryAction) => {
|
||||
location = opts.getLocation()
|
||||
subscribers.forEach((subscriber) => subscriber({ location, action }))
|
||||
}
|
||||
|
||||
const handleIndexChange = (action: SubscriberHistoryAction) => {
|
||||
if (opts.notifyOnIndexChange ?? true) notify(action)
|
||||
else location = opts.getLocation()
|
||||
}
|
||||
|
||||
const tryNavigation = async ({
|
||||
task,
|
||||
navigateOpts,
|
||||
...actionInfo
|
||||
}: TryNavigateArgs) => {
|
||||
const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false
|
||||
if (ignoreBlocker) {
|
||||
task()
|
||||
return
|
||||
}
|
||||
|
||||
const blockers = opts.getBlockers?.() ?? []
|
||||
const isPushOrReplace =
|
||||
actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'
|
||||
if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {
|
||||
for (const blocker of blockers) {
|
||||
const nextLocation = parseHref(actionInfo.path, actionInfo.state)
|
||||
const isBlocked = await blocker.blockerFn({
|
||||
currentLocation: location,
|
||||
nextLocation,
|
||||
action: actionInfo.type,
|
||||
})
|
||||
if (isBlocked) {
|
||||
opts.onBlocked?.()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task()
|
||||
}
|
||||
|
||||
return {
|
||||
get location() {
|
||||
return location
|
||||
},
|
||||
get length() {
|
||||
return opts.getLength()
|
||||
},
|
||||
subscribers,
|
||||
subscribe: (cb: (opts: SubscriberArgs) => void) => {
|
||||
subscribers.add(cb)
|
||||
|
||||
return () => {
|
||||
subscribers.delete(cb)
|
||||
}
|
||||
},
|
||||
push: (path, state, navigateOpts) => {
|
||||
const currentIndex = location.state[stateIndexKey]
|
||||
state = assignKeyAndIndex(currentIndex + 1, state)
|
||||
tryNavigation({
|
||||
task: () => {
|
||||
opts.pushState(path, state)
|
||||
notify({ type: 'PUSH' })
|
||||
},
|
||||
navigateOpts,
|
||||
type: 'PUSH',
|
||||
path,
|
||||
state,
|
||||
})
|
||||
},
|
||||
replace: (path, state, navigateOpts) => {
|
||||
const currentIndex = location.state[stateIndexKey]
|
||||
state = assignKeyAndIndex(currentIndex, state)
|
||||
tryNavigation({
|
||||
task: () => {
|
||||
opts.replaceState(path, state)
|
||||
notify({ type: 'REPLACE' })
|
||||
},
|
||||
navigateOpts,
|
||||
type: 'REPLACE',
|
||||
path,
|
||||
state,
|
||||
})
|
||||
},
|
||||
go: (index, navigateOpts) => {
|
||||
tryNavigation({
|
||||
task: () => {
|
||||
opts.go(index)
|
||||
handleIndexChange({ type: 'GO', index })
|
||||
},
|
||||
navigateOpts,
|
||||
type: 'GO',
|
||||
})
|
||||
},
|
||||
back: (navigateOpts) => {
|
||||
tryNavigation({
|
||||
task: () => {
|
||||
opts.back(navigateOpts?.ignoreBlocker ?? false)
|
||||
handleIndexChange({ type: 'BACK' })
|
||||
},
|
||||
navigateOpts,
|
||||
type: 'BACK',
|
||||
})
|
||||
},
|
||||
forward: (navigateOpts) => {
|
||||
tryNavigation({
|
||||
task: () => {
|
||||
opts.forward(navigateOpts?.ignoreBlocker ?? false)
|
||||
handleIndexChange({ type: 'FORWARD' })
|
||||
},
|
||||
navigateOpts,
|
||||
type: 'FORWARD',
|
||||
})
|
||||
},
|
||||
canGoBack: () => location.state[stateIndexKey] !== 0,
|
||||
createHref: (str) => opts.createHref(str),
|
||||
block: (blocker) => {
|
||||
if (!opts.setBlockers) return () => {}
|
||||
const blockers = opts.getBlockers?.() ?? []
|
||||
opts.setBlockers([...blockers, blocker])
|
||||
|
||||
return () => {
|
||||
const blockers = opts.getBlockers?.() ?? []
|
||||
opts.setBlockers?.(blockers.filter((b) => b !== blocker))
|
||||
}
|
||||
},
|
||||
flush: () => opts.flush?.(),
|
||||
destroy: () => opts.destroy?.(),
|
||||
notify,
|
||||
}
|
||||
}
|
||||
|
||||
function assignKeyAndIndex(index: number, state: HistoryState | undefined) {
|
||||
if (!state) {
|
||||
state = {} as HistoryState
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
key: createRandomKey(),
|
||||
[stateIndexKey]: index,
|
||||
} as ParsedHistoryState
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a history object that can be used to interact with the browser's
|
||||
* navigation. This is a lightweight API wrapping the browser's native methods.
|
||||
* It is designed to work with TanStack Router, but could be used as a standalone API as well.
|
||||
* IMPORTANT: This API implements history throttling via a microtask to prevent
|
||||
* excessive calls to the history API. In some browsers, calling history.pushState or
|
||||
* history.replaceState in quick succession can cause the browser to ignore subsequent
|
||||
* calls. This API smooths out those differences and ensures that your application
|
||||
* state will *eventually* match the browser state. In most cases, this is not a problem,
|
||||
* but if you need to ensure that the browser state is up to date, you can use the
|
||||
* `history.flush` method to immediately flush all pending state changes to the browser URL.
|
||||
* @param opts
|
||||
* @param opts.getHref A function that returns the current href (path + search + hash)
|
||||
* @param opts.createHref A function that takes a path and returns a href (path + search + hash)
|
||||
* @returns A history instance
|
||||
*/
|
||||
export function createBrowserHistory(opts?: {
|
||||
parseLocation?: () => HistoryLocation
|
||||
createHref?: (path: string) => string
|
||||
window?: any
|
||||
}): RouterHistory {
|
||||
const win =
|
||||
opts?.window ??
|
||||
(typeof document !== 'undefined' ? window : (undefined as any))
|
||||
|
||||
const originalPushState = win.history.pushState
|
||||
const originalReplaceState = win.history.replaceState
|
||||
|
||||
let blockers: Array<NavigationBlocker> = []
|
||||
const _getBlockers = () => blockers
|
||||
const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>
|
||||
(blockers = newBlockers)
|
||||
|
||||
const createHref = opts?.createHref ?? ((path) => path)
|
||||
const parseLocation =
|
||||
opts?.parseLocation ??
|
||||
(() =>
|
||||
parseHref(
|
||||
`${win.location.pathname}${win.location.search}${win.location.hash}`,
|
||||
win.history.state,
|
||||
))
|
||||
|
||||
// Ensure there is always a key to start
|
||||
if (!win.history.state?.key) {
|
||||
win.history.replaceState(
|
||||
{
|
||||
[stateIndexKey]: 0,
|
||||
key: createRandomKey(),
|
||||
},
|
||||
'',
|
||||
)
|
||||
}
|
||||
|
||||
let currentLocation = parseLocation()
|
||||
let rollbackLocation: HistoryLocation | undefined
|
||||
|
||||
let nextPopIsGo = false
|
||||
let ignoreNextPop = false
|
||||
let skipBlockerNextPop = false
|
||||
let ignoreNextBeforeUnload = false
|
||||
|
||||
const getLocation = () => currentLocation
|
||||
|
||||
let next:
|
||||
| undefined
|
||||
| {
|
||||
// This is the latest location that we were attempting to push/replace
|
||||
href: string
|
||||
// This is the latest state that we were attempting to push/replace
|
||||
state: any
|
||||
// This is the latest type that we were attempting to push/replace
|
||||
isPush: boolean
|
||||
}
|
||||
|
||||
// We need to track the current scheduled update to prevent
|
||||
// multiple updates from being scheduled at the same time.
|
||||
let scheduled: Promise<void> | undefined
|
||||
|
||||
// This function flushes the next update to the browser history
|
||||
const flush = () => {
|
||||
if (!next) {
|
||||
return
|
||||
}
|
||||
|
||||
// We need to ignore any updates to the subscribers while we update the browser history
|
||||
history._ignoreSubscribers = true
|
||||
|
||||
// Update the browser history
|
||||
;(next.isPush ? win.history.pushState : win.history.replaceState)(
|
||||
next.state,
|
||||
'',
|
||||
next.href,
|
||||
)
|
||||
|
||||
// Stop ignoring subscriber updates
|
||||
history._ignoreSubscribers = false
|
||||
|
||||
// Reset the nextIsPush flag and clear the scheduled update
|
||||
next = undefined
|
||||
scheduled = undefined
|
||||
rollbackLocation = undefined
|
||||
}
|
||||
|
||||
// This function queues up a call to update the browser history
|
||||
const queueHistoryAction = (
|
||||
type: 'push' | 'replace',
|
||||
destHref: string,
|
||||
state: any,
|
||||
) => {
|
||||
const href = createHref(destHref)
|
||||
|
||||
if (!scheduled) {
|
||||
rollbackLocation = currentLocation
|
||||
}
|
||||
|
||||
// Update the location in memory
|
||||
currentLocation = parseHref(destHref, state)
|
||||
|
||||
// Keep track of the next location we need to flush to the URL
|
||||
next = {
|
||||
href,
|
||||
state,
|
||||
isPush: next?.isPush || type === 'push',
|
||||
}
|
||||
|
||||
if (!scheduled) {
|
||||
// Schedule an update to the browser history
|
||||
scheduled = Promise.resolve().then(() => flush())
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: this function can probably be removed
|
||||
const onPushPop = (type: 'PUSH' | 'REPLACE') => {
|
||||
currentLocation = parseLocation()
|
||||
history.notify({ type })
|
||||
}
|
||||
|
||||
const onPushPopEvent = async () => {
|
||||
if (ignoreNextPop) {
|
||||
ignoreNextPop = false
|
||||
return
|
||||
}
|
||||
|
||||
const nextLocation = parseLocation()
|
||||
const delta =
|
||||
nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]
|
||||
const isForward = delta === 1
|
||||
const isBack = delta === -1
|
||||
const isGo = (!isForward && !isBack) || nextPopIsGo
|
||||
nextPopIsGo = false
|
||||
|
||||
const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'
|
||||
const notify: SubscriberHistoryAction = isGo
|
||||
? {
|
||||
type: 'GO',
|
||||
index: delta,
|
||||
}
|
||||
: {
|
||||
type: isBack ? 'BACK' : 'FORWARD',
|
||||
}
|
||||
|
||||
if (skipBlockerNextPop) {
|
||||
skipBlockerNextPop = false
|
||||
} else {
|
||||
const blockers = _getBlockers()
|
||||
if (typeof document !== 'undefined' && blockers.length) {
|
||||
for (const blocker of blockers) {
|
||||
const isBlocked = await blocker.blockerFn({
|
||||
currentLocation,
|
||||
nextLocation,
|
||||
action,
|
||||
})
|
||||
if (isBlocked) {
|
||||
ignoreNextPop = true
|
||||
win.history.go(1)
|
||||
history.notify(notify)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentLocation = parseLocation()
|
||||
history.notify(notify)
|
||||
}
|
||||
|
||||
const onBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
if (ignoreNextBeforeUnload) {
|
||||
ignoreNextBeforeUnload = false
|
||||
return
|
||||
}
|
||||
|
||||
let shouldBlock = false
|
||||
|
||||
// If one blocker has a non-disabled beforeUnload, we should block
|
||||
const blockers = _getBlockers()
|
||||
if (typeof document !== 'undefined' && blockers.length) {
|
||||
for (const blocker of blockers) {
|
||||
const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true
|
||||
if (shouldHaveBeforeUnload === true) {
|
||||
shouldBlock = true
|
||||
break
|
||||
}
|
||||
|
||||
if (
|
||||
typeof shouldHaveBeforeUnload === 'function' &&
|
||||
shouldHaveBeforeUnload() === true
|
||||
) {
|
||||
shouldBlock = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBlock) {
|
||||
e.preventDefault()
|
||||
return (e.returnValue = '')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const history = createHistory({
|
||||
getLocation,
|
||||
getLength: () => win.history.length,
|
||||
pushState: (href, state) => queueHistoryAction('push', href, state),
|
||||
replaceState: (href, state) => queueHistoryAction('replace', href, state),
|
||||
back: (ignoreBlocker) => {
|
||||
if (ignoreBlocker) skipBlockerNextPop = true
|
||||
ignoreNextBeforeUnload = true
|
||||
return win.history.back()
|
||||
},
|
||||
forward: (ignoreBlocker) => {
|
||||
if (ignoreBlocker) skipBlockerNextPop = true
|
||||
ignoreNextBeforeUnload = true
|
||||
win.history.forward()
|
||||
},
|
||||
go: (n) => {
|
||||
nextPopIsGo = true
|
||||
win.history.go(n)
|
||||
},
|
||||
createHref: (href) => createHref(href),
|
||||
flush,
|
||||
destroy: () => {
|
||||
win.history.pushState = originalPushState
|
||||
win.history.replaceState = originalReplaceState
|
||||
win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {
|
||||
capture: true,
|
||||
})
|
||||
win.removeEventListener(popStateEvent, onPushPopEvent)
|
||||
},
|
||||
onBlocked: () => {
|
||||
// If a navigation is blocked, we need to rollback the location
|
||||
// that we optimistically updated in memory.
|
||||
if (rollbackLocation && currentLocation !== rollbackLocation) {
|
||||
currentLocation = rollbackLocation
|
||||
}
|
||||
},
|
||||
getBlockers: _getBlockers,
|
||||
setBlockers: _setBlockers,
|
||||
notifyOnIndexChange: false,
|
||||
})
|
||||
|
||||
win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })
|
||||
win.addEventListener(popStateEvent, onPushPopEvent)
|
||||
|
||||
win.history.pushState = function (...args: Array<any>) {
|
||||
const res = originalPushState.apply(win.history, args as any)
|
||||
if (!history._ignoreSubscribers) onPushPop('PUSH')
|
||||
return res
|
||||
}
|
||||
|
||||
win.history.replaceState = function (...args: Array<any>) {
|
||||
const res = originalReplaceState.apply(win.history, args as any)
|
||||
if (!history._ignoreSubscribers) onPushPop('REPLACE')
|
||||
return res
|
||||
}
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
export function createHashHistory(opts?: { window?: any }): RouterHistory {
|
||||
const win =
|
||||
opts?.window ??
|
||||
(typeof document !== 'undefined' ? window : (undefined as any))
|
||||
return createBrowserHistory({
|
||||
window: win,
|
||||
parseLocation: () => {
|
||||
const hashSplit = win.location.hash.split('#').slice(1)
|
||||
const pathPart = hashSplit[0] ?? '/'
|
||||
const searchPart = win.location.search
|
||||
const hashEntries = hashSplit.slice(1)
|
||||
const hashPart =
|
||||
hashEntries.length === 0 ? '' : `#${hashEntries.join('#')}`
|
||||
const hashHref = `${pathPart}${searchPart}${hashPart}`
|
||||
return parseHref(hashHref, win.history.state)
|
||||
},
|
||||
createHref: (href) =>
|
||||
`${win.location.pathname}${win.location.search}#${href}`,
|
||||
})
|
||||
}
|
||||
|
||||
export function createMemoryHistory(
|
||||
opts: {
|
||||
initialEntries: Array<string>
|
||||
initialIndex?: number
|
||||
} = {
|
||||
initialEntries: ['/'],
|
||||
},
|
||||
): RouterHistory {
|
||||
const entries = opts.initialEntries
|
||||
let index = opts.initialIndex
|
||||
? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)
|
||||
: entries.length - 1
|
||||
const states = entries.map((_entry, index) =>
|
||||
assignKeyAndIndex(index, undefined),
|
||||
)
|
||||
|
||||
const getLocation = () => parseHref(entries[index]!, states[index])
|
||||
|
||||
return createHistory({
|
||||
getLocation,
|
||||
getLength: () => entries.length,
|
||||
pushState: (path, state) => {
|
||||
// Removes all subsequent entries after the current index to start a new branch
|
||||
if (index < entries.length - 1) {
|
||||
entries.splice(index + 1)
|
||||
states.splice(index + 1)
|
||||
}
|
||||
states.push(state)
|
||||
entries.push(path)
|
||||
index = Math.max(entries.length - 1, 0)
|
||||
},
|
||||
replaceState: (path, state) => {
|
||||
states[index] = state
|
||||
entries[index] = path
|
||||
},
|
||||
back: () => {
|
||||
index = Math.max(index - 1, 0)
|
||||
},
|
||||
forward: () => {
|
||||
index = Math.min(index + 1, entries.length - 1)
|
||||
},
|
||||
go: (n) => {
|
||||
index = Math.min(Math.max(index + n, 0), entries.length - 1)
|
||||
},
|
||||
createHref: (path) => path,
|
||||
})
|
||||
}
|
||||
|
||||
export function parseHref(
|
||||
href: string,
|
||||
state: ParsedHistoryState | undefined,
|
||||
): HistoryLocation {
|
||||
const hashIndex = href.indexOf('#')
|
||||
const searchIndex = href.indexOf('?')
|
||||
|
||||
return {
|
||||
href,
|
||||
pathname: href.substring(
|
||||
0,
|
||||
hashIndex > 0
|
||||
? searchIndex > 0
|
||||
? Math.min(hashIndex, searchIndex)
|
||||
: hashIndex
|
||||
: searchIndex > 0
|
||||
? searchIndex
|
||||
: href.length,
|
||||
),
|
||||
hash: hashIndex > -1 ? href.substring(hashIndex) : '',
|
||||
search:
|
||||
searchIndex > -1
|
||||
? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)
|
||||
: '',
|
||||
state: state || { [stateIndexKey]: 0, key: createRandomKey() },
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks co-pilot!
|
||||
function createRandomKey() {
|
||||
return (Math.random() + 1).toString(36).substring(7)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* @fileoverview `Map` to load rules lazily.
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const debug = require("debug")("eslint:rules");
|
||||
|
||||
/** @typedef {import("../../shared/types").Rule} Rule */
|
||||
|
||||
/**
|
||||
* The `Map` object that loads each rule when it's accessed.
|
||||
* @example
|
||||
* const rules = new LazyLoadingRuleMap([
|
||||
* ["eqeqeq", () => require("eqeqeq")],
|
||||
* ["semi", () => require("semi")],
|
||||
* ["no-unused-vars", () => require("no-unused-vars")]
|
||||
* ]);
|
||||
*
|
||||
* rules.get("semi"); // call `() => require("semi")` here.
|
||||
*
|
||||
* @extends {Map<string, () => Rule>}
|
||||
*/
|
||||
class LazyLoadingRuleMap extends Map {
|
||||
/**
|
||||
* Initialize this map.
|
||||
* @param {Array<[string, function(): Rule]>} loaders The rule loaders.
|
||||
*/
|
||||
constructor(loaders) {
|
||||
let remaining = loaders.length;
|
||||
|
||||
super(
|
||||
debug.enabled
|
||||
? loaders.map(([ruleId, load]) => {
|
||||
let cache = null;
|
||||
|
||||
return [
|
||||
ruleId,
|
||||
() => {
|
||||
if (!cache) {
|
||||
debug(
|
||||
"Loading rule %o (remaining=%d)",
|
||||
ruleId,
|
||||
--remaining,
|
||||
);
|
||||
cache = load();
|
||||
}
|
||||
return cache;
|
||||
},
|
||||
];
|
||||
})
|
||||
: loaders,
|
||||
);
|
||||
|
||||
// `super(...iterable)` uses `this.set()`, so disable it here.
|
||||
Object.defineProperty(LazyLoadingRuleMap.prototype, "set", {
|
||||
configurable: true,
|
||||
value: void 0,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a rule.
|
||||
* Each rule will be loaded on the first access.
|
||||
* @param {string} ruleId The rule ID to get.
|
||||
* @returns {Rule|undefined} The rule.
|
||||
*/
|
||||
get(ruleId) {
|
||||
const load = super.get(ruleId);
|
||||
|
||||
return load && load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate rules.
|
||||
* @returns {IterableIterator<Rule>} Rules.
|
||||
*/
|
||||
*values() {
|
||||
for (const load of super.values()) {
|
||||
yield load();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate rules.
|
||||
* @returns {IterableIterator<[string, Rule]>} Rules.
|
||||
*/
|
||||
*entries() {
|
||||
for (const [ruleId, load] of super.entries()) {
|
||||
yield [ruleId, load()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a function with each rule.
|
||||
* @param {Function} callbackFn The callback function.
|
||||
* @param {any} [thisArg] The object to pass to `this` of the callback function.
|
||||
* @returns {void}
|
||||
*/
|
||||
forEach(callbackFn, thisArg) {
|
||||
for (const [ruleId, load] of super.entries()) {
|
||||
callbackFn.call(thisArg, load(), ruleId, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forbid mutation.
|
||||
Object.defineProperties(LazyLoadingRuleMap.prototype, {
|
||||
clear: { configurable: true, value: void 0 },
|
||||
delete: { configurable: true, value: void 0 },
|
||||
[Symbol.iterator]: {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: LazyLoadingRuleMap.prototype.entries,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = { LazyLoadingRuleMap };
|
||||
@@ -0,0 +1,26 @@
|
||||
Copyright (c) 2013, Dominic Tarr
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = assertNode;
|
||||
var _isNode = require("../validators/isNode.js");
|
||||
function assertNode(node) {
|
||||
if (!(0, _isNode.default)(node)) {
|
||||
var _node$type;
|
||||
const type = (_node$type = node == null ? void 0 : node.type) != null ? _node$type : JSON.stringify(node);
|
||||
throw new TypeError(`Not a valid node of type "${type}"`);
|
||||
}
|
||||
}
|
||||
|
||||
//# sourceMappingURL=assertNode.js.map
|
||||
@@ -0,0 +1 @@
|
||||
module.exports={C:{"52":0.01613,"59":0.02689,"60":0.00538,"78":0.01076,"88":0.00538,"91":0.00538,"101":0.00538,"102":0.00538,"113":0.00538,"115":0.16134,"121":0.00538,"122":0.00538,"123":0.01613,"124":0.00538,"125":0.01076,"128":0.23663,"131":0.00538,"132":0.00538,"133":0.02151,"134":0.02151,"135":0.33344,"136":1.1724,"137":0.00538,_:"2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 53 54 55 56 57 58 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 79 80 81 82 83 84 85 86 87 89 90 92 93 94 95 96 97 98 99 100 103 104 105 106 107 108 109 110 111 112 114 116 117 118 119 120 126 127 129 130 138 139 140 3.5 3.6"},D:{"49":0.01076,"61":0.00538,"63":0.00538,"65":0.00538,"66":0.02151,"74":0.00538,"79":0.03227,"87":0.02689,"88":0.03227,"90":0.00538,"91":0.01613,"92":0.00538,"93":0.06991,"98":0.00538,"101":0.01076,"102":0.00538,"103":0.37646,"104":0.01613,"105":0.00538,"106":0.01613,"107":0.01076,"108":0.02689,"109":0.5378,"111":0.00538,"112":0.00538,"113":0.02689,"114":0.10218,"115":0.02151,"116":0.26352,"117":0.03227,"118":0.11294,"119":0.04302,"120":0.07529,"121":0.12907,"122":0.15058,"123":0.0484,"124":0.08605,"125":0.09143,"126":0.47864,"127":0.18285,"128":0.19899,"129":0.13983,"130":0.29041,"131":0.94115,"132":2.54379,"133":12.35864,"134":17.61833,"135":0.02689,_:"4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 50 51 52 53 54 55 56 57 58 59 60 62 64 67 68 69 70 71 72 73 75 76 77 78 80 81 83 84 85 86 89 94 95 96 97 99 100 110 136 137 138"},F:{"86":0.00538,"87":0.00538,"88":0.00538,"95":0.02151,"102":0.00538,"107":0.01076,"114":0.00538,"116":0.28503,"117":0.72603,_:"9 11 12 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 60 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 89 90 91 92 93 94 96 97 98 99 100 101 103 104 105 106 108 109 110 111 112 113 115 9.5-9.6 10.0-10.1 10.5 10.6 11.1 11.5 11.6 12.1"},B:{"17":0.00538,"92":0.00538,"102":0.00538,"109":0.05916,"112":0.03227,"113":0.00538,"119":0.00538,"120":0.00538,"121":0.01076,"122":0.02689,"123":0.00538,"124":0.00538,"125":0.01076,"126":0.01613,"127":0.01076,"128":0.01076,"129":0.01076,"130":0.01613,"131":0.07529,"132":0.16134,"133":2.31792,"134":5.23279,_:"12 13 14 15 16 18 79 80 81 83 84 85 86 87 88 89 90 91 93 94 95 96 97 98 99 100 101 103 104 105 106 107 108 110 111 114 115 116 117 118"},E:{"14":0.01613,"15":0.00538,_:"0 4 5 6 7 8 9 10 11 12 13 3.1 3.2 5.1 6.1 7.1 9.1 10.1","11.1":0.00538,"12.1":0.00538,"13.1":0.03227,"14.1":0.05378,"15.1":0.00538,"15.2-15.3":0.00538,"15.4":0.01613,"15.5":0.01613,"15.6":0.23125,"16.0":0.02689,"16.1":0.03765,"16.2":0.02689,"16.3":0.06454,"16.4":0.02689,"16.5":0.03227,"16.6":0.37108,"17.0":0.02151,"17.1":0.20436,"17.2":0.03765,"17.3":0.03227,"17.4":0.08067,"17.5":0.12907,"17.6":0.38184,"18.0":0.05378,"18.1":0.15596,"18.2":0.07529,"18.3":1.79087,"18.4":0.01613},G:{"8":0,"3.2":0,"4.0-4.1":0,"4.2-4.3":0.00459,"5.0-5.1":0,"6.0-6.1":0.01377,"7.0-7.1":0.00918,"8.1-8.4":0,"9.0-9.2":0.00689,"9.3":0.03214,"10.0-10.2":0.0023,"10.3":0.0528,"11.0-11.2":0.24335,"11.3-11.4":0.01607,"12.0-12.1":0.00918,"12.2-12.5":0.22728,"13.0-13.1":0.00459,"13.2":0.00689,"13.3":0.00918,"13.4-13.7":0.03214,"14.0-14.4":0.08035,"14.5-14.8":0.09642,"15.0-15.1":0.0528,"15.2-15.3":0.0528,"15.4":0.06428,"15.5":0.07347,"15.6-15.8":0.90454,"16.0":0.12856,"16.1":0.26401,"16.2":0.13775,"16.3":0.23876,"16.4":0.0528,"16.5":0.09872,"16.6-16.7":1.07213,"17.0":0.06428,"17.1":0.11479,"17.2":0.08724,"17.3":0.12168,"17.4":0.24335,"17.5":0.5418,"17.6-17.7":1.57261,"18.0":0.44079,"18.1":1.44175,"18.2":0.64511,"18.3":13.48313,"18.4":0.19973},P:{"4":0.04179,"20":0.01045,"21":0.03134,"22":0.0209,"23":0.0209,"24":0.0209,"25":0.0209,"26":0.07314,"27":3.38515,"5.0-5.4":0.01045,_:"6.2-6.4 8.2 9.2 10.1 11.1-11.2 12.0 13.0 14.0 15.0 16.0 17.0 18.0","7.2-7.4":0.01045,"19.0":0.01045},I:{"0":0.05075,"3":0,"4":0,"2.1":0,"2.2":0,"2.3":0,"4.1":0,"4.2-4.3":0.00002,"4.4":0,"4.4.3-4.4.4":0.00006},K:{"0":0.14794,_:"10 11 12 11.1 11.5 12.1"},A:{"11":0.01613,_:"6 7 8 9 10 5.5"},S:{_:"2.5 3.0-3.1"},J:{_:"7 10"},N:{_:"10 11"},R:{_:"0"},M:{"0":0.51315},Q:{_:"14.9"},O:{"0":0.01387},H:{"0":0},L:{"0":18.99622}};
|
||||
@@ -0,0 +1 @@
|
||||
function e(e){"@hwc/retry"===globalThis?.process?.env.DEBUG&&console.debug(e)}class RetryTask{id=Math.random().toString(36).slice(2);fn;error;timestamp=Date.now();lastAttempt=this.timestamp;resolve;reject;signal;constructor(e,t,r,i,s){this.fn=e,this.error=t,this.timestamp=Date.now(),this.lastAttempt=Date.now(),this.resolve=r,this.reject=i,this.signal=s}get age(){return Date.now()-this.timestamp}}class Retrier{#e=[];#t=[];#r=0;#i;#s;#n;#o;#c;constructor(e,{timeout:t=6e4,maxDelay:r=100,concurrency:i=1e3}={}){if("function"!=typeof e)throw new Error("Missing function to check errors");this.#o=e,this.#i=t,this.#s=r,this.#c=i}get retrying(){return this.#e.length}get pending(){return this.#t.length}get working(){return this.#r}#a(t,{signal:r,promise:i,resolve:s,reject:n}){let o;try{o=t()}catch(e){return n(new Error(`Synchronous error: ${e.message}`,{cause:e})),i}return o&&"function"==typeof o.then?(this.#r++,i.finally((()=>{this.#r--,this.#h()})).catch((()=>{})),Promise.resolve(o).then((t=>{e("Function called successfully without retry."),s(t)})).catch((i=>{if(!this.#o(i))return void n(i);const o=new RetryTask(t,i,s,n,r);e(`Function failed, queuing for retry with task ${o.id}.`),this.#e.push(o),r?.addEventListener("abort",(()=>{e(`Task ${o.id} was aborted due to AbortSignal.`),n(r.reason)})),this.#g()})),i):(n(new Error("Result is not a promise.")),i)}retry(e,{signal:t}={}){t?.throwIfAborted();const{promise:r,resolve:i,reject:s}=function(){if(Promise.withResolvers)return Promise.withResolvers();let e,t;const r=new Promise(((r,i)=>{e=r,t=i}));if(void 0===e||void 0===t)throw new Error("Promise executor did not initialize resolve or reject.");return{promise:r,resolve:e,reject:t}}();return this.#t.push((()=>this.#a(e,{signal:t,promise:r,resolve:i,reject:s}))),this.#h(),r}#u(){this.pending&&this.#h(),this.retrying&&this.#g()}#h(){e(`Processing pending tasks: ${this.pending} pending, ${this.working} working.`);const t=this.#c-this.working;if(t<=0)return;const r=Math.min(this.pending,t);for(let e=0;e<r;e++){const e=this.#t.shift();e?.()}e(`Processed pending tasks: ${this.pending} pending, ${this.working} working.`)}#g(){clearTimeout(this.#n),this.#n=void 0,e(`Processing retry queue: ${this.retrying} retrying, ${this.working} working.`);const t=()=>{this.#n=setTimeout((()=>this.#u()),0)},r=this.#e.shift();return r?function(e,t){return e.age>t}(r,this.#i)?(e(`Task ${r.id} was abandoned due to timeout.`),r.reject(r.error),void t()):function(e,t){const r=Date.now()-e.lastAttempt,i=Math.max(e.lastAttempt-e.timestamp,1);return r>=Math.min(1.2*i,t)}(r,this.#s)?(r.lastAttempt=Date.now(),void Promise.resolve(r.fn()).then((t=>{e(`Task ${r.id} succeeded after ${r.age}ms.`),r.resolve(t)})).catch((t=>{if(!this.#o(t))return e(`Task ${r.id} failed with non-retryable error: ${t.message}.`),void r.reject(t);r.lastAttempt=Date.now(),this.#e.push(r),e(`Task ${r.id} failed, requeueing to try again.`)})).finally((()=>{this.#u()}))):(e(`Task ${r.id} is not ready to retry, skipping.`),this.#e.push(r),void t()):(e("Queue is empty, exiting."),void(this.pending&&t()))}}export{Retrier};
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag use of comma operator
|
||||
* @author Brandon Mills
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Disallow comma operators",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/no-sequences",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowInParentheses: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
defaultOptions: [
|
||||
{
|
||||
allowInParentheses: true,
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedCommaExpression: "Unexpected use of comma operator.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const [{ allowInParentheses }] = context.options;
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Parts of the grammar that are required to have parens.
|
||||
*/
|
||||
const parenthesized = {
|
||||
DoWhileStatement: "test",
|
||||
IfStatement: "test",
|
||||
SwitchStatement: "discriminant",
|
||||
WhileStatement: "test",
|
||||
WithStatement: "object",
|
||||
ArrowFunctionExpression: "body",
|
||||
|
||||
/*
|
||||
* Omitting CallExpression - commas are parsed as argument separators
|
||||
* Omitting NewExpression - commas are parsed as argument separators
|
||||
* Omitting ForInStatement - parts aren't individually parenthesised
|
||||
* Omitting ForStatement - parts aren't individually parenthesised
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether a node is required by the grammar to be wrapped in
|
||||
* parens, e.g. the test of an if statement.
|
||||
* @param {ASTNode} node The AST node
|
||||
* @returns {boolean} True if parens around node belong to parent node.
|
||||
*/
|
||||
function requiresExtraParens(node) {
|
||||
return (
|
||||
node.parent &&
|
||||
parenthesized[node.parent.type] &&
|
||||
node === node.parent[parenthesized[node.parent.type]]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node is wrapped in parens.
|
||||
* @param {ASTNode} node The AST node
|
||||
* @returns {boolean} True if the node has a paren on each side.
|
||||
*/
|
||||
function isParenthesised(node) {
|
||||
return astUtils.isParenthesised(sourceCode, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node is wrapped in two levels of parens.
|
||||
* @param {ASTNode} node The AST node
|
||||
* @returns {boolean} True if two parens surround the node on each side.
|
||||
*/
|
||||
function isParenthesisedTwice(node) {
|
||||
const previousToken = sourceCode.getTokenBefore(node, 1),
|
||||
nextToken = sourceCode.getTokenAfter(node, 1);
|
||||
|
||||
return (
|
||||
isParenthesised(node) &&
|
||||
previousToken &&
|
||||
nextToken &&
|
||||
astUtils.isOpeningParenToken(previousToken) &&
|
||||
previousToken.range[1] <= node.range[0] &&
|
||||
astUtils.isClosingParenToken(nextToken) &&
|
||||
nextToken.range[0] >= node.range[1]
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
SequenceExpression(node) {
|
||||
// Always allow sequences in for statement update
|
||||
if (
|
||||
node.parent.type === "ForStatement" &&
|
||||
(node === node.parent.init || node === node.parent.update)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrapping a sequence in extra parens indicates intent
|
||||
if (allowInParentheses) {
|
||||
if (requiresExtraParens(node)) {
|
||||
if (isParenthesisedTwice(node)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (isParenthesised(node)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const firstCommaToken = sourceCode.getTokenAfter(
|
||||
node.expressions[0],
|
||||
astUtils.isCommaToken,
|
||||
);
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: firstCommaToken.loc,
|
||||
messageId: "unexpectedCommaExpression",
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
module.exports={A:{A:{"2":"K D E F A B mC"},B:{"1":"0 9 Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I","2":"C L M G N O P"},C:{"1":"0 9 eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB MC wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC oC pC","2":"1 2 3 4 5 6 7 8 nC LC J PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB qC rC","194":"YB ZB aB bB cB dB"},D:{"1":"0 9 YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB MC wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC","2":"1 2 3 4 5 6 7 8 J PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB"},E:{"1":"A B C L M G TC FC GC xC yC zC UC VC HC 0C IC WC XC YC ZC aC 1C JC bC cC dC eC fC 2C KC gC hC iC jC 3C","2":"J PB K D E F sC SC tC uC vC wC"},F:{"1":"0 3 4 5 6 7 8 RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB wB xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z","2":"1 2 F B C G N O P QB 4C 5C 6C 7C FC kC 8C GC"},G:{"1":"GD HD ID JD KD LD MD ND OD PD QD RD SD UC VC HC TD IC WC XC YC ZC aC UD JC bC cC dC eC fC VD KC gC hC iC jC","2":"E SC 9C lC AD BD CD DD ED FD"},H:{"2":"WD"},I:{"1":"I","2":"LC J XD YD ZD aD lC bD cD"},J:{"2":"D A"},K:{"1":"H","2":"A B C FC kC GC"},L:{"1":"I"},M:{"1":"EC"},N:{"2":"A B"},O:{"1":"HC"},P:{"1":"1 2 3 4 5 6 7 8 J dD eD fD gD hD TC iD jD kD lD mD IC JC KC nD"},Q:{"1":"oD"},R:{"1":"pD"},S:{"1":"qD rD"}},B:5,C:"CSS Font Loading",D:true};
|
||||
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
'use client';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const react_1 = require("react");
|
||||
const documentContext = (0, react_1.createContext)(null);
|
||||
exports.default = documentContext;
|
||||
@@ -0,0 +1,34 @@
|
||||
export type IL10n = import("./interfaces").IL10n;
|
||||
export const GenericL10n: null;
|
||||
/** @typedef {import("./interfaces").IL10n} IL10n */
|
||||
/**
|
||||
* NOTE: The L10n-implementations should use lowercase language-codes
|
||||
* internally.
|
||||
* @implements {IL10n}
|
||||
*/
|
||||
export class L10n implements IL10n {
|
||||
static "__#62@#fixupLangCode"(langCode: any): any;
|
||||
static "__#62@#isRTL"(lang: any): boolean;
|
||||
constructor({ lang, isRTL }: {
|
||||
lang: any;
|
||||
isRTL: any;
|
||||
}, l10n?: null);
|
||||
_setL10n(l10n: any): void;
|
||||
/** @inheritdoc */
|
||||
getLanguage(): any;
|
||||
/** @inheritdoc */
|
||||
getDirection(): string;
|
||||
/** @inheritdoc */
|
||||
get(ids: any, args: null | undefined, fallback: any): Promise<any>;
|
||||
/** @inheritdoc */
|
||||
translate(element: any): Promise<void>;
|
||||
/** @inheritdoc */
|
||||
translateOnce(element: any): Promise<void>;
|
||||
/** @inheritdoc */
|
||||
destroy(): Promise<void>;
|
||||
/** @inheritdoc */
|
||||
pause(): void;
|
||||
/** @inheritdoc */
|
||||
resume(): void;
|
||||
#private;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
name: ci
|
||||
'on':
|
||||
- push
|
||||
- pull_request
|
||||
jobs:
|
||||
test:
|
||||
name: Node ${{ matrix.node }} / ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
node:
|
||||
- '14'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm install
|
||||
- run: npm run build --if-present
|
||||
- run: npm test
|
||||
@@ -0,0 +1,956 @@
|
||||
// Copied from `@types/prettier`
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/5bb07fc4b087cb7ee91084afa6fe750551a7bbb1/types/prettier/index.d.ts
|
||||
|
||||
// Minimum TypeScript Version: 4.2
|
||||
|
||||
// Add `export {}` here to shut off automatic exporting from index.d.ts. There
|
||||
// are quite a few utility types here that don't need to be shipped with the
|
||||
// exported module.
|
||||
export {};
|
||||
|
||||
import { builders, printer, utils } from "./doc.js";
|
||||
|
||||
export namespace doc {
|
||||
export { builders, printer, utils };
|
||||
}
|
||||
|
||||
// This utility is here to handle the case where you have an explicit union
|
||||
// between string literals and the generic string type. It would normally
|
||||
// resolve out to just the string type, but this generic LiteralUnion maintains
|
||||
// the intellisense of the original union.
|
||||
//
|
||||
// It comes from this issue: microsoft/TypeScript#29729:
|
||||
// https://github.com/microsoft/TypeScript/issues/29729#issuecomment-700527227
|
||||
export type LiteralUnion<T extends U, U = string> =
|
||||
| T
|
||||
| (Pick<U, never> & { _?: never | undefined });
|
||||
|
||||
export type AST = any;
|
||||
export type Doc = doc.builders.Doc;
|
||||
|
||||
// The type of elements that make up the given array T.
|
||||
type ArrayElement<T> = T extends Array<infer E> ? E : never;
|
||||
|
||||
// A union of the properties of the given object that are arrays.
|
||||
type ArrayProperties<T> = {
|
||||
[K in keyof T]: NonNullable<T[K]> extends readonly any[] ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
// A union of the properties of the given array T that can be used to index it.
|
||||
// If the array is a tuple, then that's going to be the explicit indices of the
|
||||
// array, otherwise it's going to just be number.
|
||||
type IndexProperties<T extends { length: number }> =
|
||||
IsTuple<T> extends true ? Exclude<Partial<T>["length"], T["length"]> : number;
|
||||
|
||||
// Effectively performing T[P], except that it's telling TypeScript that it's
|
||||
// safe to do this for tuples, arrays, or objects.
|
||||
type IndexValue<T, P> = T extends any[]
|
||||
? P extends number
|
||||
? T[P]
|
||||
: never
|
||||
: P extends keyof T
|
||||
? T[P]
|
||||
: never;
|
||||
|
||||
// Determines if an object T is an array like string[] (in which case this
|
||||
// evaluates to false) or a tuple like [string] (in which case this evaluates to
|
||||
// true).
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
type IsTuple<T> = T extends []
|
||||
? true
|
||||
: T extends [infer First, ...infer Remain]
|
||||
? IsTuple<Remain>
|
||||
: false;
|
||||
|
||||
type CallProperties<T> = T extends any[] ? IndexProperties<T> : keyof T;
|
||||
type IterProperties<T> = T extends any[]
|
||||
? IndexProperties<T>
|
||||
: ArrayProperties<T>;
|
||||
|
||||
type CallCallback<T, U> = (path: AstPath<T>, index: number, value: any) => U;
|
||||
type EachCallback<T> = (
|
||||
path: AstPath<ArrayElement<T>>,
|
||||
index: number,
|
||||
value: any,
|
||||
) => void;
|
||||
type MapCallback<T, U> = (
|
||||
path: AstPath<ArrayElement<T>>,
|
||||
index: number,
|
||||
value: any,
|
||||
) => U;
|
||||
|
||||
// https://github.com/prettier/prettier/blob/next/src/common/ast-path.js
|
||||
export class AstPath<T = any> {
|
||||
constructor(value: T);
|
||||
|
||||
get key(): string | null;
|
||||
get index(): number | null;
|
||||
get node(): T;
|
||||
get parent(): T | null;
|
||||
get grandparent(): T | null;
|
||||
get isInArray(): boolean;
|
||||
get siblings(): T[] | null;
|
||||
get next(): T | null;
|
||||
get previous(): T | null;
|
||||
get isFirst(): boolean;
|
||||
get isLast(): boolean;
|
||||
get isRoot(): boolean;
|
||||
get root(): T;
|
||||
get ancestors(): T[];
|
||||
|
||||
stack: T[];
|
||||
|
||||
callParent<U>(callback: (path: this) => U, count?: number): U;
|
||||
|
||||
/**
|
||||
* @deprecated Please use `AstPath#key` or `AstPath#index`
|
||||
*/
|
||||
getName(): PropertyKey | null;
|
||||
|
||||
/**
|
||||
* @deprecated Please use `AstPath#node` or `AstPath#siblings`
|
||||
*/
|
||||
getValue(): T;
|
||||
|
||||
getNode(count?: number): T | null;
|
||||
|
||||
getParentNode(count?: number): T | null;
|
||||
|
||||
match(
|
||||
...predicates: Array<
|
||||
(node: any, name: string | null, number: number | null) => boolean
|
||||
>
|
||||
): boolean;
|
||||
|
||||
// For each of the tree walk functions (call, each, and map) this provides 5
|
||||
// strict type signatures, along with a fallback at the end if you end up
|
||||
// calling more than 5 properties deep. This helps a lot with typing because
|
||||
// for the majority of cases you're calling fewer than 5 properties, so the
|
||||
// tree walk functions have a clearer understanding of what you're doing.
|
||||
//
|
||||
// Note that resolving these types is somewhat complicated, and it wasn't
|
||||
// even supported until TypeScript 4.2 (before it would just say that the
|
||||
// type instantiation was excessively deep and possibly infinite).
|
||||
|
||||
call<U>(callback: CallCallback<T, U>): U;
|
||||
call<U, P1 extends CallProperties<T>>(
|
||||
callback: CallCallback<IndexValue<T, P1>, U>,
|
||||
prop1: P1,
|
||||
): U;
|
||||
call<U, P1 extends keyof T, P2 extends CallProperties<T[P1]>>(
|
||||
callback: CallCallback<IndexValue<IndexValue<T, P1>, P2>, U>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
): U;
|
||||
call<
|
||||
U,
|
||||
P1 extends keyof T,
|
||||
P2 extends CallProperties<T[P1]>,
|
||||
P3 extends CallProperties<IndexValue<T[P1], P2>>,
|
||||
>(
|
||||
callback: CallCallback<
|
||||
IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>,
|
||||
U
|
||||
>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
prop3: P3,
|
||||
): U;
|
||||
call<
|
||||
U,
|
||||
P1 extends keyof T,
|
||||
P2 extends CallProperties<T[P1]>,
|
||||
P3 extends CallProperties<IndexValue<T[P1], P2>>,
|
||||
P4 extends CallProperties<IndexValue<IndexValue<T[P1], P2>, P3>>,
|
||||
>(
|
||||
callback: CallCallback<
|
||||
IndexValue<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>, P4>,
|
||||
U
|
||||
>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
prop3: P3,
|
||||
prop4: P4,
|
||||
): U;
|
||||
call<U, P extends PropertyKey>(
|
||||
callback: CallCallback<any, U>,
|
||||
prop1: P,
|
||||
prop2: P,
|
||||
prop3: P,
|
||||
prop4: P,
|
||||
...props: P[]
|
||||
): U;
|
||||
|
||||
each(callback: EachCallback<T>): void;
|
||||
each<P1 extends IterProperties<T>>(
|
||||
callback: EachCallback<IndexValue<T, P1>>,
|
||||
prop1: P1,
|
||||
): void;
|
||||
each<P1 extends keyof T, P2 extends IterProperties<T[P1]>>(
|
||||
callback: EachCallback<IndexValue<IndexValue<T, P1>, P2>>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
): void;
|
||||
each<
|
||||
P1 extends keyof T,
|
||||
P2 extends IterProperties<T[P1]>,
|
||||
P3 extends IterProperties<IndexValue<T[P1], P2>>,
|
||||
>(
|
||||
callback: EachCallback<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
prop3: P3,
|
||||
): void;
|
||||
each<
|
||||
P1 extends keyof T,
|
||||
P2 extends IterProperties<T[P1]>,
|
||||
P3 extends IterProperties<IndexValue<T[P1], P2>>,
|
||||
P4 extends IterProperties<IndexValue<IndexValue<T[P1], P2>, P3>>,
|
||||
>(
|
||||
callback: EachCallback<
|
||||
IndexValue<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>, P4>
|
||||
>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
prop3: P3,
|
||||
prop4: P4,
|
||||
): void;
|
||||
each(
|
||||
callback: EachCallback<any[]>,
|
||||
prop1: PropertyKey,
|
||||
prop2: PropertyKey,
|
||||
prop3: PropertyKey,
|
||||
prop4: PropertyKey,
|
||||
...props: PropertyKey[]
|
||||
): void;
|
||||
|
||||
map<U>(callback: MapCallback<T, U>): U[];
|
||||
map<U, P1 extends IterProperties<T>>(
|
||||
callback: MapCallback<IndexValue<T, P1>, U>,
|
||||
prop1: P1,
|
||||
): U[];
|
||||
map<U, P1 extends keyof T, P2 extends IterProperties<T[P1]>>(
|
||||
callback: MapCallback<IndexValue<IndexValue<T, P1>, P2>, U>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
): U[];
|
||||
map<
|
||||
U,
|
||||
P1 extends keyof T,
|
||||
P2 extends IterProperties<T[P1]>,
|
||||
P3 extends IterProperties<IndexValue<T[P1], P2>>,
|
||||
>(
|
||||
callback: MapCallback<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>, U>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
prop3: P3,
|
||||
): U[];
|
||||
map<
|
||||
U,
|
||||
P1 extends keyof T,
|
||||
P2 extends IterProperties<T[P1]>,
|
||||
P3 extends IterProperties<IndexValue<T[P1], P2>>,
|
||||
P4 extends IterProperties<IndexValue<IndexValue<T[P1], P2>, P3>>,
|
||||
>(
|
||||
callback: MapCallback<
|
||||
IndexValue<IndexValue<IndexValue<IndexValue<T, P1>, P2>, P3>, P4>,
|
||||
U
|
||||
>,
|
||||
prop1: P1,
|
||||
prop2: P2,
|
||||
prop3: P3,
|
||||
prop4: P4,
|
||||
): U[];
|
||||
map<U>(
|
||||
callback: MapCallback<any[], U>,
|
||||
prop1: PropertyKey,
|
||||
prop2: PropertyKey,
|
||||
prop3: PropertyKey,
|
||||
prop4: PropertyKey,
|
||||
...props: PropertyKey[]
|
||||
): U[];
|
||||
}
|
||||
|
||||
/** @deprecated `FastPath` was renamed to `AstPath` */
|
||||
export type FastPath<T = any> = AstPath<T>;
|
||||
|
||||
export type BuiltInParser = (text: string, options?: any) => AST;
|
||||
export type BuiltInParserName =
|
||||
| "acorn"
|
||||
| "angular"
|
||||
| "babel-flow"
|
||||
| "babel-ts"
|
||||
| "babel"
|
||||
| "css"
|
||||
| "espree"
|
||||
| "flow"
|
||||
| "glimmer"
|
||||
| "graphql"
|
||||
| "html"
|
||||
| "json-stringify"
|
||||
| "json"
|
||||
| "json5"
|
||||
| "jsonc"
|
||||
| "less"
|
||||
| "lwc"
|
||||
| "markdown"
|
||||
| "mdx"
|
||||
| "meriyah"
|
||||
| "scss"
|
||||
| "typescript"
|
||||
| "vue"
|
||||
| "yaml";
|
||||
export type BuiltInParsers = Record<BuiltInParserName, BuiltInParser>;
|
||||
|
||||
/**
|
||||
* For use in `.prettierrc.js`, `.prettierrc.ts`, `.prettierrc.cjs`, `.prettierrc.cts`, `prettierrc.mjs`, `prettierrc.mts`, `prettier.config.js`, `prettier.config.ts`, `prettier.config.cjs`, `prettier.config.cts`, `prettier.config.mjs`, `prettier.config.mts`
|
||||
*/
|
||||
export interface Config extends Options {
|
||||
overrides?: Array<{
|
||||
files: string | string[];
|
||||
excludeFiles?: string | string[];
|
||||
options?: Options;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface Options extends Partial<RequiredOptions> {}
|
||||
|
||||
export interface RequiredOptions extends doc.printer.Options {
|
||||
/**
|
||||
* Print semicolons at the ends of statements.
|
||||
* @default true
|
||||
*/
|
||||
semi: boolean;
|
||||
/**
|
||||
* Use single quotes instead of double quotes.
|
||||
* @default false
|
||||
*/
|
||||
singleQuote: boolean;
|
||||
/**
|
||||
* Use single quotes in JSX.
|
||||
* @default false
|
||||
*/
|
||||
jsxSingleQuote: boolean;
|
||||
/**
|
||||
* Print trailing commas wherever possible.
|
||||
* @default "all"
|
||||
*/
|
||||
trailingComma: "none" | "es5" | "all";
|
||||
/**
|
||||
* Print spaces between brackets in object literals.
|
||||
* @default true
|
||||
*/
|
||||
bracketSpacing: boolean;
|
||||
/**
|
||||
* How to wrap object literals.
|
||||
* @default "preserve"
|
||||
*/
|
||||
objectWrap: "preserve" | "collapse";
|
||||
/**
|
||||
* Put the `>` of a multi-line HTML (HTML, JSX, Vue, Angular) element at the end of the last line instead of being
|
||||
* alone on the next line (does not apply to self closing elements).
|
||||
* @default false
|
||||
*/
|
||||
bracketSameLine: boolean;
|
||||
/**
|
||||
* Format only a segment of a file.
|
||||
* @default 0
|
||||
*/
|
||||
rangeStart: number;
|
||||
/**
|
||||
* Format only a segment of a file.
|
||||
* @default Number.POSITIVE_INFINITY
|
||||
*/
|
||||
rangeEnd: number;
|
||||
/**
|
||||
* Specify which parser to use.
|
||||
*/
|
||||
parser: LiteralUnion<BuiltInParserName>;
|
||||
/**
|
||||
* Specify the input filepath. This will be used to do parser inference.
|
||||
*/
|
||||
filepath: string;
|
||||
/**
|
||||
* Prettier can restrict itself to only format files that contain a special comment, called a pragma, at the top of the file.
|
||||
* This is very useful when gradually transitioning large, unformatted codebases to prettier.
|
||||
* @default false
|
||||
*/
|
||||
requirePragma: boolean;
|
||||
/**
|
||||
* Prettier can insert a special @format marker at the top of files specifying that
|
||||
* the file has been formatted with prettier. This works well when used in tandem with
|
||||
* the --require-pragma option. If there is already a docblock at the top of
|
||||
* the file then this option will add a newline to it with the @format marker.
|
||||
* @default false
|
||||
*/
|
||||
insertPragma: boolean;
|
||||
/**
|
||||
* By default, Prettier will wrap markdown text as-is since some services use a linebreak-sensitive renderer.
|
||||
* In some cases you may want to rely on editor/viewer soft wrapping instead, so this option allows you to opt out.
|
||||
* @default "preserve"
|
||||
*/
|
||||
proseWrap: "always" | "never" | "preserve";
|
||||
/**
|
||||
* Include parentheses around a sole arrow function parameter.
|
||||
* @default "always"
|
||||
*/
|
||||
arrowParens: "avoid" | "always";
|
||||
/**
|
||||
* Provide ability to support new languages to prettier.
|
||||
*/
|
||||
plugins: Array<string | Plugin>;
|
||||
/**
|
||||
* How to handle whitespaces in HTML.
|
||||
* @default "css"
|
||||
*/
|
||||
htmlWhitespaceSensitivity: "css" | "strict" | "ignore";
|
||||
/**
|
||||
* Which end of line characters to apply.
|
||||
* @default "lf"
|
||||
*/
|
||||
endOfLine: "auto" | "lf" | "crlf" | "cr";
|
||||
/**
|
||||
* Change when properties in objects are quoted.
|
||||
* @default "as-needed"
|
||||
*/
|
||||
quoteProps: "as-needed" | "consistent" | "preserve";
|
||||
/**
|
||||
* Whether or not to indent the code inside <script> and <style> tags in Vue files.
|
||||
* @default false
|
||||
*/
|
||||
vueIndentScriptAndStyle: boolean;
|
||||
/**
|
||||
* Control whether Prettier formats quoted code embedded in the file.
|
||||
* @default "auto"
|
||||
*/
|
||||
embeddedLanguageFormatting: "auto" | "off";
|
||||
/**
|
||||
* Enforce single attribute per line in HTML, Vue and JSX.
|
||||
* @default false
|
||||
*/
|
||||
singleAttributePerLine: boolean;
|
||||
/**
|
||||
* Where to print operators when binary expressions wrap lines.
|
||||
* @default "end"
|
||||
*/
|
||||
experimentalOperatorPosition: "start" | "end";
|
||||
/**
|
||||
* Use curious ternaries, with the question mark after the condition, instead
|
||||
* of on the same line as the consequent.
|
||||
* @default false
|
||||
*/
|
||||
experimentalTernaries: boolean;
|
||||
/**
|
||||
* Put the `>` of a multi-line JSX element at the end of the last line instead of being alone on the next line.
|
||||
* @default false
|
||||
* @deprecated use bracketSameLine instead
|
||||
*/
|
||||
jsxBracketSameLine?: boolean;
|
||||
/**
|
||||
* Arbitrary additional values on an options object are always allowed.
|
||||
*/
|
||||
[_: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ParserOptions<T = any> extends RequiredOptions {
|
||||
locStart: (node: T) => number;
|
||||
locEnd: (node: T) => number;
|
||||
originalText: string;
|
||||
}
|
||||
|
||||
export interface Plugin<T = any> {
|
||||
languages?: SupportLanguage[] | undefined;
|
||||
parsers?: { [parserName: string]: Parser<T> } | undefined;
|
||||
printers?: { [astFormat: string]: Printer<T> } | undefined;
|
||||
options?: SupportOptions | undefined;
|
||||
defaultOptions?: Partial<RequiredOptions> | undefined;
|
||||
}
|
||||
|
||||
export interface Parser<T = any> {
|
||||
parse: (text: string, options: ParserOptions<T>) => T | Promise<T>;
|
||||
astFormat: string;
|
||||
hasPragma?: ((text: string) => boolean) | undefined;
|
||||
locStart: (node: T) => number;
|
||||
locEnd: (node: T) => number;
|
||||
preprocess?:
|
||||
| ((text: string, options: ParserOptions<T>) => string)
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export interface Printer<T = any> {
|
||||
print(
|
||||
path: AstPath<T>,
|
||||
options: ParserOptions<T>,
|
||||
print: (path: AstPath<T>) => Doc,
|
||||
args?: unknown,
|
||||
): Doc;
|
||||
embed?:
|
||||
| ((
|
||||
path: AstPath,
|
||||
options: Options,
|
||||
) =>
|
||||
| ((
|
||||
textToDoc: (text: string, options: Options) => Promise<Doc>,
|
||||
print: (
|
||||
selector?: string | number | Array<string | number> | AstPath,
|
||||
) => Doc,
|
||||
path: AstPath,
|
||||
options: Options,
|
||||
) => Promise<Doc | undefined> | Doc | undefined)
|
||||
| Doc
|
||||
| null)
|
||||
| undefined;
|
||||
preprocess?:
|
||||
| ((ast: T, options: ParserOptions<T>) => T | Promise<T>)
|
||||
| undefined;
|
||||
insertPragma?: (text: string) => string;
|
||||
/**
|
||||
* @returns `null` if you want to remove this node
|
||||
* @returns `void` if you want to use modified `cloned`
|
||||
* @returns anything if you want to replace the node with it
|
||||
*/
|
||||
massageAstNode?:
|
||||
| ((original: any, cloned: any, parent: any) => any)
|
||||
| undefined;
|
||||
hasPrettierIgnore?: ((path: AstPath<T>) => boolean) | undefined;
|
||||
canAttachComment?: ((node: T) => boolean) | undefined;
|
||||
isBlockComment?: ((node: T) => boolean) | undefined;
|
||||
willPrintOwnComments?: ((path: AstPath<T>) => boolean) | undefined;
|
||||
printComment?:
|
||||
| ((commentPath: AstPath<T>, options: ParserOptions<T>) => Doc)
|
||||
| undefined;
|
||||
/**
|
||||
* By default, Prettier searches all object properties (except for a few predefined ones) of each node recursively.
|
||||
* This function can be provided to override that behavior.
|
||||
* @param node The node whose children should be returned.
|
||||
* @param options Current options.
|
||||
* @returns `[]` if the node has no children or `undefined` to fall back on the default behavior.
|
||||
*/
|
||||
getCommentChildNodes?:
|
||||
| ((node: T, options: ParserOptions<T>) => T[] | undefined)
|
||||
| undefined;
|
||||
handleComments?:
|
||||
| {
|
||||
ownLine?:
|
||||
| ((
|
||||
commentNode: any,
|
||||
text: string,
|
||||
options: ParserOptions<T>,
|
||||
ast: T,
|
||||
isLastComment: boolean,
|
||||
) => boolean)
|
||||
| undefined;
|
||||
endOfLine?:
|
||||
| ((
|
||||
commentNode: any,
|
||||
text: string,
|
||||
options: ParserOptions<T>,
|
||||
ast: T,
|
||||
isLastComment: boolean,
|
||||
) => boolean)
|
||||
| undefined;
|
||||
remaining?:
|
||||
| ((
|
||||
commentNode: any,
|
||||
text: string,
|
||||
options: ParserOptions<T>,
|
||||
ast: T,
|
||||
isLastComment: boolean,
|
||||
) => boolean)
|
||||
| undefined;
|
||||
}
|
||||
| undefined;
|
||||
getVisitorKeys?:
|
||||
| ((node: T, nonTraversableKeys: Set<string>) => string[])
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export interface CursorOptions extends Options {
|
||||
/**
|
||||
* Specify where the cursor is.
|
||||
*/
|
||||
cursorOffset: number;
|
||||
}
|
||||
|
||||
export interface CursorResult {
|
||||
formatted: string;
|
||||
cursorOffset: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* `format` is used to format text using Prettier. [Options](https://prettier.io/docs/options) may be provided to override the defaults.
|
||||
*/
|
||||
export function format(source: string, options?: Options): Promise<string>;
|
||||
|
||||
/**
|
||||
* `check` checks to see if the file has been formatted with Prettier given those options and returns a `Boolean`.
|
||||
* This is similar to the `--list-different` parameter in the CLI and is useful for running Prettier in CI scenarios.
|
||||
*/
|
||||
export function check(source: string, options?: Options): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* `formatWithCursor` both formats the code, and translates a cursor position from unformatted code to formatted code.
|
||||
* This is useful for editor integrations, to prevent the cursor from moving when code is formatted.
|
||||
*
|
||||
* The `cursorOffset` option should be provided, to specify where the cursor is.
|
||||
*/
|
||||
export function formatWithCursor(
|
||||
source: string,
|
||||
options: CursorOptions,
|
||||
): Promise<CursorResult>;
|
||||
|
||||
export interface ResolveConfigOptions {
|
||||
/**
|
||||
* If set to `false`, all caching will be bypassed.
|
||||
*/
|
||||
useCache?: boolean | undefined;
|
||||
/**
|
||||
* Pass directly the path of the config file if you don't wish to search for it.
|
||||
*/
|
||||
config?: string | undefined;
|
||||
/**
|
||||
* If set to `true` and an `.editorconfig` file is in your project,
|
||||
* Prettier will parse it and convert its properties to the corresponding prettier configuration.
|
||||
* This configuration will be overridden by `.prettierrc`, etc. Currently,
|
||||
* the following EditorConfig properties are supported:
|
||||
* - indent_style
|
||||
* - indent_size/tab_width
|
||||
* - max_line_length
|
||||
*/
|
||||
editorconfig?: boolean | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* `resolveConfig` can be used to resolve configuration for a given source file,
|
||||
* passing its path or url as the first argument. The config search will start at
|
||||
* the directory of the file location and continue to search up the directory.
|
||||
*
|
||||
* A promise is returned which will resolve to:
|
||||
*
|
||||
* - An options object, providing a [config file](https://prettier.io/docs/configuration) was found.
|
||||
* - `null`, if no file was found.
|
||||
*
|
||||
* The promise will be rejected if there was an error parsing the configuration file.
|
||||
*/
|
||||
export function resolveConfig(
|
||||
fileUrlOrPath: string | URL,
|
||||
options?: ResolveConfigOptions,
|
||||
): Promise<Options | null>;
|
||||
|
||||
/**
|
||||
* `resolveConfigFile` can be used to find the path of the Prettier configuration file,
|
||||
* that will be used when resolving the config (i.e. when calling `resolveConfig`).
|
||||
*
|
||||
* A promise is returned which will resolve to:
|
||||
*
|
||||
* - The path of the configuration file.
|
||||
* - `null`, if no file was found.
|
||||
*
|
||||
* The promise will be rejected if there was an error parsing the configuration file.
|
||||
*/
|
||||
export function resolveConfigFile(
|
||||
fileUrlOrPath?: string | URL,
|
||||
): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* As you repeatedly call `resolveConfig`, the file system structure will be cached for performance. This function will clear the cache.
|
||||
* Generally this is only needed for editor integrations that know that the file system has changed since the last format took place.
|
||||
*/
|
||||
export function clearConfigCache(): Promise<void>;
|
||||
|
||||
export interface SupportLanguage {
|
||||
name: string;
|
||||
since?: string | undefined;
|
||||
parsers: BuiltInParserName[] | string[];
|
||||
group?: string | undefined;
|
||||
tmScope?: string | undefined;
|
||||
aceMode?: string | undefined;
|
||||
codemirrorMode?: string | undefined;
|
||||
codemirrorMimeType?: string | undefined;
|
||||
aliases?: string[] | undefined;
|
||||
extensions?: string[] | undefined;
|
||||
filenames?: string[] | undefined;
|
||||
linguistLanguageId?: number | undefined;
|
||||
vscodeLanguageIds?: string[] | undefined;
|
||||
interpreters?: string[] | undefined;
|
||||
}
|
||||
|
||||
export interface SupportOptionRange {
|
||||
start: number;
|
||||
end: number;
|
||||
step: number;
|
||||
}
|
||||
|
||||
export type SupportOptionType =
|
||||
| "int"
|
||||
| "string"
|
||||
| "boolean"
|
||||
| "choice"
|
||||
| "path";
|
||||
|
||||
export type CoreCategoryType =
|
||||
| "Config"
|
||||
| "Editor"
|
||||
| "Format"
|
||||
| "Other"
|
||||
| "Output"
|
||||
| "Global"
|
||||
| "Special";
|
||||
|
||||
export interface BaseSupportOption<Type extends SupportOptionType> {
|
||||
readonly name?: string | undefined;
|
||||
/**
|
||||
* Usually you can use {@link CoreCategoryType}
|
||||
*/
|
||||
category: string;
|
||||
/**
|
||||
* The type of the option.
|
||||
*
|
||||
* When passing a type other than the ones listed below, the option is
|
||||
* treated as taking any string as argument, and `--option <${type}>` will
|
||||
* be displayed in --help.
|
||||
*/
|
||||
type: Type;
|
||||
/**
|
||||
* Indicate that the option is deprecated.
|
||||
*
|
||||
* Use a string to add an extra message to --help for the option,
|
||||
* for example to suggest a replacement option.
|
||||
*/
|
||||
deprecated?: true | string | undefined;
|
||||
/**
|
||||
* Description to be displayed in --help. If omitted, the option won't be
|
||||
* shown at all in --help.
|
||||
*/
|
||||
description?: string | undefined;
|
||||
}
|
||||
|
||||
export interface IntSupportOption extends BaseSupportOption<"int"> {
|
||||
default?: number | undefined;
|
||||
array?: false | undefined;
|
||||
range?: SupportOptionRange | undefined;
|
||||
}
|
||||
|
||||
export interface IntArraySupportOption extends BaseSupportOption<"int"> {
|
||||
default?: Array<{ value: number[] }> | undefined;
|
||||
array: true;
|
||||
}
|
||||
|
||||
export interface StringSupportOption extends BaseSupportOption<"string"> {
|
||||
default?: string | undefined;
|
||||
array?: false | undefined;
|
||||
}
|
||||
|
||||
export interface StringArraySupportOption extends BaseSupportOption<"string"> {
|
||||
default?: Array<{ value: string[] }> | undefined;
|
||||
array: true;
|
||||
}
|
||||
|
||||
export interface BooleanSupportOption extends BaseSupportOption<"boolean"> {
|
||||
default?: boolean | undefined;
|
||||
array?: false | undefined;
|
||||
description: string;
|
||||
oppositeDescription?: string | undefined;
|
||||
}
|
||||
|
||||
export interface BooleanArraySupportOption
|
||||
extends BaseSupportOption<"boolean"> {
|
||||
default?: Array<{ value: boolean[] }> | undefined;
|
||||
array: true;
|
||||
}
|
||||
|
||||
export interface ChoiceSupportOption<Value = any>
|
||||
extends BaseSupportOption<"choice"> {
|
||||
default?: Value | Array<{ value: Value }> | undefined;
|
||||
description: string;
|
||||
choices: Array<{
|
||||
since?: string | undefined;
|
||||
value: Value;
|
||||
description: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface PathSupportOption extends BaseSupportOption<"path"> {
|
||||
default?: string | undefined;
|
||||
array?: false | undefined;
|
||||
}
|
||||
|
||||
export interface PathArraySupportOption extends BaseSupportOption<"path"> {
|
||||
default?: Array<{ value: string[] }> | undefined;
|
||||
array: true;
|
||||
}
|
||||
|
||||
export type SupportOption =
|
||||
| IntSupportOption
|
||||
| IntArraySupportOption
|
||||
| StringSupportOption
|
||||
| StringArraySupportOption
|
||||
| BooleanSupportOption
|
||||
| BooleanArraySupportOption
|
||||
| ChoiceSupportOption
|
||||
| PathSupportOption
|
||||
| PathArraySupportOption;
|
||||
|
||||
export interface SupportOptions extends Record<string, SupportOption> {}
|
||||
|
||||
export interface SupportInfo {
|
||||
languages: SupportLanguage[];
|
||||
options: SupportOption[];
|
||||
}
|
||||
|
||||
export interface FileInfoOptions {
|
||||
ignorePath?: string | URL | (string | URL)[] | undefined;
|
||||
withNodeModules?: boolean | undefined;
|
||||
plugins?: Array<string | Plugin> | undefined;
|
||||
resolveConfig?: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface FileInfoResult {
|
||||
ignored: boolean;
|
||||
inferredParser: string | null;
|
||||
}
|
||||
|
||||
export function getFileInfo(
|
||||
file: string | URL,
|
||||
options?: FileInfoOptions,
|
||||
): Promise<FileInfoResult>;
|
||||
|
||||
export interface SupportInfoOptions {
|
||||
plugins?: Array<string | Plugin> | undefined;
|
||||
showDeprecated?: boolean | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object representing the parsers, languages and file types Prettier supports for the current version.
|
||||
*/
|
||||
export function getSupportInfo(
|
||||
options?: SupportInfoOptions,
|
||||
): Promise<SupportInfo>;
|
||||
|
||||
/**
|
||||
* `version` field in `package.json`
|
||||
*/
|
||||
export const version: string;
|
||||
|
||||
// https://github.com/prettier/prettier/blob/next/src/utils/public.js
|
||||
export namespace util {
|
||||
interface SkipOptions {
|
||||
backwards?: boolean | undefined;
|
||||
}
|
||||
|
||||
type Quote = "'" | '"';
|
||||
|
||||
function getMaxContinuousCount(text: string, searchString: string): number;
|
||||
|
||||
function getStringWidth(text: string): number;
|
||||
|
||||
function getAlignmentSize(
|
||||
text: string,
|
||||
tabWidth: number,
|
||||
startIndex?: number | undefined,
|
||||
): number;
|
||||
|
||||
function getIndentSize(value: string, tabWidth: number): number;
|
||||
|
||||
function skipNewline(
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
options?: SkipOptions | undefined,
|
||||
): number | false;
|
||||
|
||||
function skipInlineComment(
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
): number | false;
|
||||
|
||||
function skipTrailingComment(
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
): number | false;
|
||||
|
||||
function skipTrailingComment(
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
): number | false;
|
||||
|
||||
function hasNewline(
|
||||
text: string,
|
||||
startIndex: number,
|
||||
options?: SkipOptions | undefined,
|
||||
): boolean;
|
||||
|
||||
function hasNewlineInRange(
|
||||
text: string,
|
||||
startIndex: number,
|
||||
endIndex: number,
|
||||
): boolean;
|
||||
|
||||
function hasSpaces(
|
||||
text: string,
|
||||
startIndex: number,
|
||||
options?: SkipOptions | undefined,
|
||||
): boolean;
|
||||
|
||||
function getNextNonSpaceNonCommentCharacterIndex(
|
||||
text: string,
|
||||
startIndex: number,
|
||||
): number | false;
|
||||
|
||||
function getNextNonSpaceNonCommentCharacter(
|
||||
text: string,
|
||||
startIndex: number,
|
||||
): string;
|
||||
|
||||
function isNextLineEmpty(text: string, startIndex: number): boolean;
|
||||
|
||||
function isPreviousLineEmpty(text: string, startIndex: number): boolean;
|
||||
|
||||
function makeString(
|
||||
rawText: string,
|
||||
enclosingQuote: Quote,
|
||||
unescapeUnnecessaryEscapes?: boolean | undefined,
|
||||
): string;
|
||||
|
||||
function skip(
|
||||
characters: string | RegExp,
|
||||
): (
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
options?: SkipOptions,
|
||||
) => number | false;
|
||||
|
||||
const skipWhitespace: (
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
options?: SkipOptions,
|
||||
) => number | false;
|
||||
|
||||
const skipSpaces: (
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
options?: SkipOptions,
|
||||
) => number | false;
|
||||
|
||||
const skipToLineEnd: (
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
options?: SkipOptions,
|
||||
) => number | false;
|
||||
|
||||
const skipEverythingButNewLine: (
|
||||
text: string,
|
||||
startIndex: number | false,
|
||||
options?: SkipOptions,
|
||||
) => number | false;
|
||||
|
||||
function addLeadingComment(node: any, comment: any): void;
|
||||
|
||||
function addDanglingComment(node: any, comment: any, marker: any): void;
|
||||
|
||||
function addTrailingComment(node: any, comment: any): void;
|
||||
|
||||
function getPreferredQuote(
|
||||
text: string,
|
||||
preferredQuoteOrPreferSingleQuote: Quote | boolean,
|
||||
): Quote;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
module.exports={A:{A:{"2":"K D E F A B mC"},B:{"1":"0 9 N O P Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I","2":"C L M G"},C:{"1":"0 9 uB vB MC wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC oC pC","2":"1 2 3 4 5 6 7 8 nC LC J PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB qC rC"},D:{"1":"0 9 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC","2":"1 2 3 4 5 6 7 8 J PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB MC wB NC xB yB zB 0B"},E:{"1":"L M G GC xC yC zC UC VC HC 0C IC WC XC YC ZC aC 1C JC bC cC dC eC fC 2C KC gC hC iC jC 3C","2":"J PB K D E F A B sC SC tC uC vC wC TC","130":"C FC"},F:{"1":"0 qB rB sB tB uB vB wB xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z","2":"1 2 3 4 5 6 7 8 F B C G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB 4C 5C 6C 7C FC kC 8C GC"},G:{"1":"JD KD LD MD ND OD PD QD RD SD UC VC HC TD IC WC XC YC ZC aC UD JC bC cC dC eC fC VD KC gC hC iC jC","2":"E SC 9C lC AD BD CD DD ED FD GD HD ID"},H:{"2":"WD"},I:{"1":"I","2":"LC J XD YD ZD aD lC bD cD"},J:{"2":"D A"},K:{"1":"H","2":"A B C FC kC GC"},L:{"1":"I"},M:{"1":"EC"},N:{"2":"A B"},O:{"1":"HC"},P:{"1":"1 2 3 4 5 6 7 8 hD TC iD jD kD lD mD IC JC KC nD","2":"J dD eD fD gD"},Q:{"1":"oD"},R:{"1":"pD"},S:{"1":"rD","2":"qD"}},B:1,C:"AbortController & AbortSignal",D:true};
|
||||
@@ -0,0 +1,3 @@
|
||||
const compareBuild = require('./compare-build')
|
||||
const sort = (list, loose) => list.sort((a, b) => compareBuild(a, b, loose))
|
||||
module.exports = sort
|
||||
@@ -0,0 +1,476 @@
|
||||
// @ts-self-types="./retrier.d.ts"
|
||||
/**
|
||||
* @fileoverview A utility for retrying failed async method calls.
|
||||
*/
|
||||
|
||||
/* global setTimeout, clearTimeout */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const MAX_TASK_TIMEOUT = 60000;
|
||||
const MAX_TASK_DELAY = 100;
|
||||
const MAX_CONCURRENCY = 1000;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Logs a message to the console if the DEBUG environment variable is set.
|
||||
* @param {string} message The message to log.
|
||||
* @returns {void}
|
||||
*/
|
||||
function debug(message) {
|
||||
if (globalThis?.process?.env.DEBUG === "@hwc/retry") {
|
||||
console.debug(message);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The following logic has been extracted from graceful-fs.
|
||||
*
|
||||
* The ISC License
|
||||
*
|
||||
* Copyright (c) 2011-2023 Isaac Z. Schlueter, Ben Noordhuis, and Contributors
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if it is time to retry a task based on the timestamp and last attempt time.
|
||||
* @param {RetryTask} task The task to check.
|
||||
* @param {number} maxDelay The maximum delay for the queue.
|
||||
* @returns {boolean} true if it is time to retry, false otherwise.
|
||||
*/
|
||||
function isTimeToRetry(task, maxDelay) {
|
||||
const timeSinceLastAttempt = Date.now() - task.lastAttempt;
|
||||
const timeSinceStart = Math.max(task.lastAttempt - task.timestamp, 1);
|
||||
const desiredDelay = Math.min(timeSinceStart * 1.2, maxDelay);
|
||||
|
||||
return timeSinceLastAttempt >= desiredDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it is time to bail out based on the given timestamp.
|
||||
* @param {RetryTask} task The task to check.
|
||||
* @param {number} timeout The timeout for the queue.
|
||||
* @returns {boolean} true if it is time to bail, false otherwise.
|
||||
*/
|
||||
function isTimeToBail(task, timeout) {
|
||||
return task.age > timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new promise with resolve and reject functions.
|
||||
* @returns {{promise:Promise<any>, resolve:(value:any) => any, reject: (value:any) => any}} A new promise.
|
||||
*/
|
||||
function createPromise() {
|
||||
if (Promise.withResolvers) {
|
||||
return Promise.withResolvers();
|
||||
}
|
||||
|
||||
let resolve, reject;
|
||||
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
|
||||
if (resolve === undefined || reject === undefined) {
|
||||
throw new Error("Promise executor did not initialize resolve or reject.");
|
||||
}
|
||||
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A class to represent a task in the retry queue.
|
||||
*/
|
||||
class RetryTask {
|
||||
|
||||
/**
|
||||
* The unique ID for the task.
|
||||
* @type {string}
|
||||
*/
|
||||
id = Math.random().toString(36).slice(2);
|
||||
|
||||
/**
|
||||
* The function to call.
|
||||
* @type {Function}
|
||||
*/
|
||||
fn;
|
||||
|
||||
/**
|
||||
* The error that was thrown.
|
||||
* @type {Error}
|
||||
*/
|
||||
error;
|
||||
|
||||
/**
|
||||
* The timestamp of the task.
|
||||
* @type {number}
|
||||
*/
|
||||
timestamp = Date.now();
|
||||
|
||||
/**
|
||||
* The timestamp of the last attempt.
|
||||
* @type {number}
|
||||
*/
|
||||
lastAttempt = this.timestamp;
|
||||
|
||||
/**
|
||||
* The resolve function for the promise.
|
||||
* @type {Function}
|
||||
*/
|
||||
resolve;
|
||||
|
||||
/**
|
||||
* The reject function for the promise.
|
||||
* @type {Function}
|
||||
*/
|
||||
reject;
|
||||
|
||||
/**
|
||||
* The AbortSignal to monitor for cancellation.
|
||||
* @type {AbortSignal|undefined}
|
||||
*/
|
||||
signal;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Function} fn The function to call.
|
||||
* @param {Error} error The error that was thrown.
|
||||
* @param {Function} resolve The resolve function for the promise.
|
||||
* @param {Function} reject The reject function for the promise.
|
||||
* @param {AbortSignal|undefined} signal The AbortSignal to monitor for cancellation.
|
||||
*/
|
||||
constructor(fn, error, resolve, reject, signal) {
|
||||
this.fn = fn;
|
||||
this.error = error;
|
||||
this.timestamp = Date.now();
|
||||
this.lastAttempt = Date.now();
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
this.signal = signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the age of the task.
|
||||
* @returns {number} The age of the task in milliseconds.
|
||||
* @readonly
|
||||
*/
|
||||
get age() {
|
||||
return Date.now() - this.timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A class that manages a queue of retry jobs.
|
||||
*/
|
||||
class Retrier {
|
||||
|
||||
/**
|
||||
* Represents the queue for processing tasks.
|
||||
* @type {Array<RetryTask>}
|
||||
*/
|
||||
#retrying = [];
|
||||
|
||||
/**
|
||||
* Represents the queue for pending tasks.
|
||||
* @type {Array<Function>}
|
||||
*/
|
||||
#pending = [];
|
||||
|
||||
/**
|
||||
* The number of tasks currently being processed.
|
||||
* @type {number}
|
||||
*/
|
||||
#working = 0;
|
||||
|
||||
/**
|
||||
* The timeout for the queue.
|
||||
* @type {number}
|
||||
*/
|
||||
#timeout;
|
||||
|
||||
/**
|
||||
* The maximum delay for the queue.
|
||||
* @type {number}
|
||||
*/
|
||||
#maxDelay;
|
||||
|
||||
/**
|
||||
* The setTimeout() timer ID.
|
||||
* @type {NodeJS.Timeout|undefined}
|
||||
*/
|
||||
#timerId;
|
||||
|
||||
/**
|
||||
* The function to call.
|
||||
* @type {Function}
|
||||
*/
|
||||
#check;
|
||||
|
||||
/**
|
||||
* The maximum number of concurrent tasks.
|
||||
* @type {number}
|
||||
*/
|
||||
#concurrency;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Function} check The function to call.
|
||||
* @param {object} [options] The options for the instance.
|
||||
* @param {number} [options.timeout] The timeout for the queue.
|
||||
* @param {number} [options.maxDelay] The maximum delay for the queue.
|
||||
* @param {number} [options.concurrency] The maximum number of concurrent tasks.
|
||||
*/
|
||||
constructor(check, { timeout = MAX_TASK_TIMEOUT, maxDelay = MAX_TASK_DELAY, concurrency = MAX_CONCURRENCY } = {}) {
|
||||
|
||||
if (typeof check !== "function") {
|
||||
throw new Error("Missing function to check errors");
|
||||
}
|
||||
|
||||
this.#check = check;
|
||||
this.#timeout = timeout;
|
||||
this.#maxDelay = maxDelay;
|
||||
this.#concurrency = concurrency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of tasks waiting to be retried.
|
||||
* @returns {number} The number of tasks in the retry queue.
|
||||
*/
|
||||
get retrying() {
|
||||
return this.#retrying.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of tasks waiting to be processed in the pending queue.
|
||||
* @returns {number} The number of tasks in the pending queue.
|
||||
*/
|
||||
get pending() {
|
||||
return this.#pending.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of tasks currently being processed.
|
||||
* @returns {number} The number of tasks currently being processed.
|
||||
*/
|
||||
get working() {
|
||||
return this.#working;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the function and retries if it fails.
|
||||
* @param {Function} fn The function to call.
|
||||
* @param {Object} options The options for the job.
|
||||
* @param {AbortSignal} [options.signal] The AbortSignal to monitor for cancellation.
|
||||
* @param {Promise<any>} options.promise The promise to return when the function settles.
|
||||
* @param {Function} options.resolve The resolve function for the promise.
|
||||
* @param {Function} options.reject The reject function for the promise.
|
||||
* @returns {Promise<any>} A promise that resolves when the function is
|
||||
* called successfully.
|
||||
*/
|
||||
#call(fn, { signal, promise, resolve, reject }) {
|
||||
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = fn();
|
||||
} catch (/** @type {any} */ error) {
|
||||
reject(new Error(`Synchronous error: ${error.message}`, { cause: error }));
|
||||
return promise;
|
||||
}
|
||||
|
||||
// if the result is not a promise then reject an error
|
||||
if (!result || typeof result.then !== "function") {
|
||||
reject(new Error("Result is not a promise."));
|
||||
return promise;
|
||||
}
|
||||
|
||||
this.#working++;
|
||||
promise.finally(() => {
|
||||
this.#working--;
|
||||
this.#processPending();
|
||||
})
|
||||
// `promise.finally` creates a new promise that may be rejected, so it must be handled.
|
||||
.catch(() => { });
|
||||
|
||||
// call the original function and catch any ENFILE or EMFILE errors
|
||||
Promise.resolve(result)
|
||||
.then(value => {
|
||||
debug("Function called successfully without retry.");
|
||||
resolve(value);
|
||||
})
|
||||
.catch(error => {
|
||||
if (!this.#check(error)) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const task = new RetryTask(fn, error, resolve, reject, signal);
|
||||
|
||||
debug(`Function failed, queuing for retry with task ${task.id}.`);
|
||||
this.#retrying.push(task);
|
||||
|
||||
signal?.addEventListener("abort", () => {
|
||||
debug(`Task ${task.id} was aborted due to AbortSignal.`);
|
||||
reject(signal.reason);
|
||||
});
|
||||
|
||||
this.#processQueue();
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new retry job to the queue.
|
||||
* @param {Function} fn The function to call.
|
||||
* @param {object} [options] The options for the job.
|
||||
* @param {AbortSignal} [options.signal] The AbortSignal to monitor for cancellation.
|
||||
* @returns {Promise<any>} A promise that resolves when the queue is
|
||||
* processed.
|
||||
*/
|
||||
retry(fn, { signal } = {}) {
|
||||
|
||||
signal?.throwIfAborted();
|
||||
|
||||
const { promise, resolve, reject } = createPromise();
|
||||
|
||||
this.#pending.push(() => this.#call(fn, { signal, promise, resolve, reject }));
|
||||
this.#processPending();
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes the pending queue and the retry queue.
|
||||
* @returns {void}
|
||||
*/
|
||||
#processAll() {
|
||||
if (this.pending) {
|
||||
this.#processPending();
|
||||
}
|
||||
|
||||
if (this.retrying) {
|
||||
this.#processQueue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the pending queue to see which tasks can be started.
|
||||
* @returns {void}
|
||||
*/
|
||||
#processPending() {
|
||||
|
||||
debug(`Processing pending tasks: ${this.pending} pending, ${this.working} working.`);
|
||||
|
||||
const available = this.#concurrency - this.working;
|
||||
|
||||
if (available <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const count = Math.min(this.pending, available);
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const task = this.#pending.shift();
|
||||
task?.();
|
||||
}
|
||||
|
||||
debug(`Processed pending tasks: ${this.pending} pending, ${this.working} working.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the queue.
|
||||
* @returns {void}
|
||||
*/
|
||||
#processQueue() {
|
||||
// clear any timer because we're going to check right now
|
||||
clearTimeout(this.#timerId);
|
||||
this.#timerId = undefined;
|
||||
|
||||
debug(`Processing retry queue: ${this.retrying} retrying, ${this.working} working.`);
|
||||
|
||||
const processAgain = () => {
|
||||
this.#timerId = setTimeout(() => this.#processAll(), 0);
|
||||
};
|
||||
|
||||
// if there's nothing in the queue, we're done
|
||||
const task = this.#retrying.shift();
|
||||
if (!task) {
|
||||
debug("Queue is empty, exiting.");
|
||||
|
||||
if (this.pending) {
|
||||
processAgain();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if it's time to bail, then bail
|
||||
if (isTimeToBail(task, this.#timeout)) {
|
||||
debug(`Task ${task.id} was abandoned due to timeout.`);
|
||||
task.reject(task.error);
|
||||
processAgain();
|
||||
return;
|
||||
}
|
||||
|
||||
// if it's not time to retry, then wait and try again
|
||||
if (!isTimeToRetry(task, this.#maxDelay)) {
|
||||
debug(`Task ${task.id} is not ready to retry, skipping.`);
|
||||
this.#retrying.push(task);
|
||||
processAgain();
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, try again
|
||||
task.lastAttempt = Date.now();
|
||||
|
||||
// Promise.resolve needed in case it's a thenable but not a Promise
|
||||
Promise.resolve(task.fn())
|
||||
// @ts-ignore because we know it's any
|
||||
.then(result => {
|
||||
debug(`Task ${task.id} succeeded after ${task.age}ms.`);
|
||||
task.resolve(result);
|
||||
})
|
||||
|
||||
// @ts-ignore because we know it's any
|
||||
.catch(error => {
|
||||
if (!this.#check(error)) {
|
||||
debug(`Task ${task.id} failed with non-retryable error: ${error.message}.`);
|
||||
task.reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the task timestamp and push to back of queue to try again
|
||||
task.lastAttempt = Date.now();
|
||||
this.#retrying.push(task);
|
||||
debug(`Task ${task.id} failed, requeueing to try again.`);
|
||||
})
|
||||
.finally(() => {
|
||||
this.#processAll();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { Retrier };
|
||||
Reference in New Issue
Block a user