WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag use of an lexical declarations inside a case clause
|
||||
* @author Erik Arvidsson
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Disallow lexical declarations in case clauses",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-case-declarations",
|
||||
},
|
||||
|
||||
hasSuggestions: true,
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
addBrackets: "Add {} brackets around the case block.",
|
||||
unexpected: "Unexpected lexical declaration in case block.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
/**
|
||||
* Checks whether or not a node is a lexical declaration.
|
||||
* @param {ASTNode} node A direct child statement of a switch case.
|
||||
* @returns {boolean} Whether or not the node is a lexical declaration.
|
||||
*/
|
||||
function isLexicalDeclaration(node) {
|
||||
switch (node.type) {
|
||||
case "FunctionDeclaration":
|
||||
case "ClassDeclaration":
|
||||
return true;
|
||||
case "VariableDeclaration":
|
||||
return node.kind !== "var";
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
SwitchCase(node) {
|
||||
for (let i = 0; i < node.consequent.length; i++) {
|
||||
const statement = node.consequent[i];
|
||||
|
||||
if (isLexicalDeclaration(statement)) {
|
||||
context.report({
|
||||
node: statement,
|
||||
messageId: "unexpected",
|
||||
suggest: [
|
||||
{
|
||||
messageId: "addBrackets",
|
||||
fix: fixer => [
|
||||
fixer.insertTextBefore(
|
||||
node.consequent[0],
|
||||
"{ ",
|
||||
),
|
||||
fixer.insertTextAfter(
|
||||
node.consequent.at(-1),
|
||||
" }",
|
||||
),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
/// <reference path="./importMeta.d.ts" />
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/45096
|
||||
// TypeScript has a bug that makes <reference types="vite/types/importMeta" />
|
||||
// not possible in userland. This file provides a workaround for now.
|
||||
@@ -0,0 +1,2 @@
|
||||
export declare var SyntaxKind: any;
|
||||
//# sourceMappingURL=syntaxKind.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
var s=Object.defineProperty;var i=(t,r)=>s(t,"name",{value:r,configurable:!0});import{r as p}from"../../register-C4vWVmug.mjs";import n from"node:crypto";import"../../get-pipe-path-_tAJyU_v.mjs";import{r as c,i as u,c as d}from"../../register-C9AniqUt.mjs";import"../../require-CywAB2e6.mjs";import{i as f,e as g}from"../../node-features-JeyyvQz6.mjs";import"node:module";import"node:worker_threads";import"node:url";import"node:fs";import"node:fs/promises";import"../../index-DQtFPMc2.mjs";import"node:path";import"esbuild";import"node:os";import"../../temporary-directory-BDDVQOvU.mjs";import"../../client-D_mPDF5S.mjs";import"node:net";import"module";import"fs";import"os";import"path";import"node:util";import"../../index-gbaejti9.mjs";const U=i((t,r)=>{if(!r||typeof r=="object"&&!r.parentURL)throw new Error("The current file path (import.meta.url) must be provided in the second argument of tsImport()");const o=typeof r=="string",e=o?r:r.parentURL,m=n.randomUUID(),a=c({namespace:m});return!f(g)&&!u.test(t)&&d.test(t)?Promise.resolve(a.require(t,e)):p({namespace:m,...o?{}:r}).import(t,e)},"tsImport");export{p as register,U as tsImport};
|
||||
@@ -0,0 +1,42 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const flatted = require('flatted');
|
||||
|
||||
function tryParse(filePath, defaultValue) {
|
||||
let result;
|
||||
try {
|
||||
result = readJSON(filePath);
|
||||
} catch (ex) {
|
||||
result = defaultValue;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read json file synchronously using flatted
|
||||
*
|
||||
* @param {String} filePath Json filepath
|
||||
* @returns {*} parse result
|
||||
*/
|
||||
function readJSON(filePath) {
|
||||
return flatted.parse(
|
||||
fs.readFileSync(filePath, {
|
||||
encoding: 'utf8',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write json file synchronously using circular-json
|
||||
*
|
||||
* @param {String} filePath Json filepath
|
||||
* @param {*} data Object to serialize
|
||||
*/
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), {
|
||||
recursive: true,
|
||||
});
|
||||
fs.writeFileSync(filePath, flatted.stringify(data));
|
||||
}
|
||||
|
||||
module.exports = { tryParse, readJSON, writeJSON };
|
||||
@@ -0,0 +1,55 @@
|
||||
export { r as runBaseTests, s as setupEnvironment } from './chunks/base.B6Opl8PE.js';
|
||||
export { i as init } from './chunks/init.k9zZ9sLh.js';
|
||||
import 'node:vm';
|
||||
import '@vitest/spy';
|
||||
import './chunks/index.DXx9Dtk7.js';
|
||||
import '@vitest/expect';
|
||||
import 'node:async_hooks';
|
||||
import './chunks/setup-common.DYx3LtFI.js';
|
||||
import './chunks/coverage.CTzCuANN.js';
|
||||
import '@vitest/snapshot';
|
||||
import '@vitest/utils/timers';
|
||||
import './chunks/utils.BX5Fg8C4.js';
|
||||
import './chunks/rpc.MzXet3jl.js';
|
||||
import './chunks/index.Chj8NDwU.js';
|
||||
import './chunks/test.DNmyFkvJ.js';
|
||||
import '@vitest/runner';
|
||||
import '@vitest/utils/helpers';
|
||||
import './chunks/benchmark.CX_oY03V.js';
|
||||
import '@vitest/runner/utils';
|
||||
import '@vitest/utils/error';
|
||||
import 'pathe';
|
||||
import '@vitest/utils/offset';
|
||||
import '@vitest/utils/source-map';
|
||||
import './chunks/_commonjsHelpers.D26ty3Ew.js';
|
||||
import './chunks/nativeModuleRunner.BIakptoF.js';
|
||||
import 'node:url';
|
||||
import './chunks/index.BCY_7LL2.js';
|
||||
import 'node:fs';
|
||||
import 'node:module';
|
||||
import 'node:path';
|
||||
import 'node:process';
|
||||
import 'node:fs/promises';
|
||||
import 'node:assert';
|
||||
import 'node:v8';
|
||||
import 'node:util';
|
||||
import 'vite/module-runner';
|
||||
import './chunks/traces.DT5aQ62U.js';
|
||||
import './chunks/evaluatedModules.Dg1zASAC.js';
|
||||
import './chunks/startVitestModuleRunner.DB-7oCpn.js';
|
||||
import './chunks/modules.BJuCwlRJ.js';
|
||||
import './path.js';
|
||||
import './module-evaluator.js';
|
||||
import '@vitest/mocker';
|
||||
import '@vitest/mocker/redirect';
|
||||
import 'node:perf_hooks';
|
||||
import './chunks/inspector.CvyFGlXm.js';
|
||||
import 'node:timers';
|
||||
import 'node:timers/promises';
|
||||
import '@vitest/utils/constants';
|
||||
import './chunks/index.DdgEv5B1.js';
|
||||
import 'expect-type';
|
||||
import './chunks/index.DC7d2Pf8.js';
|
||||
import 'node:console';
|
||||
import '@vitest/utils/serialize';
|
||||
import 'tinyrainbow';
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
import type { LibDefinition } from '../variable';
|
||||
export declare const es2015_promise: LibDefinition;
|
||||
@@ -0,0 +1,5 @@
|
||||
import assertClassBrand from "./assertClassBrand.js";
|
||||
function _classPrivateFieldSet2(s, a, r) {
|
||||
return s.set(assertClassBrand(s, a), r), r;
|
||||
}
|
||||
export { _classPrivateFieldSet2 as default };
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"espree.d.cts","sourceRoot":"","sources":["../espree.cts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,SAAS,MAAM,CAAC"}
|
||||
@@ -0,0 +1,137 @@
|
||||
'use strict'
|
||||
|
||||
let MapGenerator = require('./map-generator')
|
||||
let parse = require('./parse')
|
||||
let Result = require('./result')
|
||||
let stringify = require('./stringify')
|
||||
let warnOnce = require('./warn-once')
|
||||
|
||||
class NoWorkResult {
|
||||
get content() {
|
||||
return this.result.css
|
||||
}
|
||||
|
||||
get css() {
|
||||
return this.result.css
|
||||
}
|
||||
|
||||
get map() {
|
||||
return this.result.map
|
||||
}
|
||||
|
||||
get messages() {
|
||||
return []
|
||||
}
|
||||
|
||||
get opts() {
|
||||
return this.result.opts
|
||||
}
|
||||
|
||||
get processor() {
|
||||
return this.result.processor
|
||||
}
|
||||
|
||||
get root() {
|
||||
if (this._root) {
|
||||
return this._root
|
||||
}
|
||||
|
||||
let root
|
||||
let parser = parse
|
||||
|
||||
try {
|
||||
root = parser(this._css, this._opts)
|
||||
} catch (error) {
|
||||
this.error = error
|
||||
}
|
||||
|
||||
if (this.error) {
|
||||
throw this.error
|
||||
} else {
|
||||
this._root = root
|
||||
return root
|
||||
}
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return 'NoWorkResult'
|
||||
}
|
||||
|
||||
constructor(processor, css, opts) {
|
||||
css = css.toString()
|
||||
this.stringified = false
|
||||
|
||||
this._processor = processor
|
||||
this._css = css
|
||||
this._opts = opts
|
||||
this._map = undefined
|
||||
|
||||
let str = stringify
|
||||
this.result = new Result(this._processor, undefined, this._opts)
|
||||
this.result.css = css
|
||||
|
||||
let self = this
|
||||
Object.defineProperty(this.result, 'root', {
|
||||
get() {
|
||||
return self.root
|
||||
}
|
||||
})
|
||||
|
||||
let map = new MapGenerator(str, undefined, this._opts, css)
|
||||
if (map.isMap()) {
|
||||
let [generatedCSS, generatedMap] = map.generate()
|
||||
if (generatedCSS) {
|
||||
this.result.css = generatedCSS
|
||||
}
|
||||
if (generatedMap) {
|
||||
this.result.map = generatedMap
|
||||
}
|
||||
} else {
|
||||
map.clearAnnotation()
|
||||
this.result.css = map.css
|
||||
}
|
||||
}
|
||||
|
||||
async() {
|
||||
if (this.error) return Promise.reject(this.error)
|
||||
return Promise.resolve(this.result)
|
||||
}
|
||||
|
||||
catch(onRejected) {
|
||||
return this.async().catch(onRejected)
|
||||
}
|
||||
|
||||
finally(onFinally) {
|
||||
return this.async().then(onFinally, onFinally)
|
||||
}
|
||||
|
||||
sync() {
|
||||
if (this.error) throw this.error
|
||||
return this.result
|
||||
}
|
||||
|
||||
then(onFulfilled, onRejected) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!('from' in this._opts)) {
|
||||
warnOnce(
|
||||
'Without `from` option PostCSS could generate wrong source map ' +
|
||||
'and will not find Browserslist config. Set it to CSS file path ' +
|
||||
'or to `undefined` to prevent this warning.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return this.async().then(onFulfilled, onRejected)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this._css
|
||||
}
|
||||
|
||||
warnings() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NoWorkResult
|
||||
NoWorkResult.default = NoWorkResult
|
||||
@@ -0,0 +1,352 @@
|
||||
import { expect, expectTypeOf, test } from "vitest";
|
||||
import * as z from "zod/mini";
|
||||
|
||||
const FAIL = { success: false };
|
||||
|
||||
test("z.string", async () => {
|
||||
const a = z.string();
|
||||
expect(z.parse(a, "hello")).toEqual("hello");
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
expect(() => z.parse(a, false)).toThrow();
|
||||
type a = z.infer<typeof a>;
|
||||
expectTypeOf<a>().toEqualTypeOf<string>();
|
||||
});
|
||||
|
||||
// test("z.string with description", () => {
|
||||
// const a = z.string({ description: "string description" });
|
||||
// a._def;
|
||||
// expect(a._def.description).toEqual("string description");
|
||||
// });
|
||||
|
||||
test("z.string with custom error", () => {
|
||||
const a = z.string({ error: () => "BAD" });
|
||||
expect(z.safeParse(a, 123).error!.issues[0].message).toEqual("BAD");
|
||||
});
|
||||
|
||||
test("inference in checks", () => {
|
||||
const a = z.string().check(z.refine((val) => val.length));
|
||||
z.parse(a, "___");
|
||||
expect(() => z.parse(a, "")).toThrow();
|
||||
const b = z.string().check(z.refine((val) => val.length));
|
||||
z.parse(b, "___");
|
||||
expect(() => z.parse(b, "")).toThrow();
|
||||
const c = z.string().check(z.refine((val) => val.length));
|
||||
z.parse(c, "___");
|
||||
expect(() => z.parse(c, "")).toThrow();
|
||||
const d = z.string().check(z.refine((val) => val.length));
|
||||
z.parse(d, "___");
|
||||
expect(() => z.parse(d, "")).toThrow();
|
||||
});
|
||||
|
||||
test("z.string async", async () => {
|
||||
// async
|
||||
const a = z.string().check(z.refine(async (val) => val.length));
|
||||
expect(await z.parseAsync(a, "___")).toEqual("___");
|
||||
await expect(() => z.parseAsync(a, "")).rejects.toThrowError();
|
||||
});
|
||||
|
||||
test("z.uuid", () => {
|
||||
const a = z.uuid();
|
||||
// parse uuid
|
||||
z.parse(a, "550e8400-e29b-41d4-a716-446655440000");
|
||||
z.parse(a, "550e8400-e29b-61d4-a716-446655440000");
|
||||
|
||||
// bad uuid
|
||||
expect(() => z.parse(a, "hello")).toThrow();
|
||||
// wrong type
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
|
||||
const b = z.uuidv4();
|
||||
z.parse(b, "550e8400-e29b-41d4-a716-446655440000");
|
||||
expect(z.safeParse(b, "550e8400-e29b-61d4-a716-446655440000")).toMatchObject(FAIL);
|
||||
|
||||
const c = z.uuidv6();
|
||||
z.parse(c, "550e8400-e29b-61d4-a716-446655440000");
|
||||
expect(z.safeParse(c, "550e8400-e29b-41d4-a716-446655440000")).toMatchObject(FAIL);
|
||||
|
||||
const d = z.uuidv7();
|
||||
z.parse(d, "550e8400-e29b-71d4-a716-446655440000");
|
||||
expect(z.safeParse(d, "550e8400-e29b-41d4-a716-446655440000")).toMatchObject(FAIL);
|
||||
expect(z.safeParse(d, "550e8400-e29b-61d4-a716-446655440000")).toMatchObject(FAIL);
|
||||
});
|
||||
|
||||
test("z.email", () => {
|
||||
const a = z.email();
|
||||
expect(z.parse(a, "test@test.com")).toEqual("test@test.com");
|
||||
expect(() => z.parse(a, "test")).toThrow();
|
||||
expect(z.safeParse(a, "bad email", { error: () => "bad email" }).error!.issues[0].message).toEqual("bad email");
|
||||
|
||||
const b = z.email("bad email");
|
||||
expect(z.safeParse(b, "bad email").error!.issues[0].message).toEqual("bad email");
|
||||
|
||||
const c = z.email({ error: "bad email" });
|
||||
expect(z.safeParse(c, "bad email").error!.issues[0].message).toEqual("bad email");
|
||||
|
||||
const d = z.email({ error: () => "bad email" });
|
||||
expect(z.safeParse(d, "bad email").error!.issues[0].message).toEqual("bad email");
|
||||
});
|
||||
|
||||
test("z.url", () => {
|
||||
const a = z.url();
|
||||
// valid URLs
|
||||
expect(a.parse("http://example.com")).toEqual("http://example.com");
|
||||
expect(a.parse("https://example.com")).toEqual("https://example.com");
|
||||
expect(a.parse("ftp://example.com")).toEqual("ftp://example.com");
|
||||
expect(a.parse("http://sub.example.com")).toEqual("http://sub.example.com");
|
||||
expect(a.parse("https://example.com/path?query=123#fragment")).toEqual("https://example.com/path?query=123#fragment");
|
||||
expect(a.parse("http://localhost")).toEqual("http://localhost");
|
||||
expect(a.parse("https://localhost")).toEqual("https://localhost");
|
||||
expect(a.parse("http://localhost:3000")).toEqual("http://localhost:3000");
|
||||
expect(a.parse("https://localhost:3000")).toEqual("https://localhost:3000");
|
||||
|
||||
// test trimming
|
||||
expect(a.parse(" http://example.com ")).toEqual("http://example.com");
|
||||
expect(a.parse(" http://example.com/")).toEqual("http://example.com/");
|
||||
expect(a.parse(" http://example.com")).toEqual("http://example.com");
|
||||
expect(a.parse(" http://example.com//")).toEqual("http://example.com//");
|
||||
|
||||
// invalid URLs
|
||||
expect(() => a.parse("not-a-url")).toThrow();
|
||||
// expect(() => a.parse("http:/example.com")).toThrow();
|
||||
expect(() => a.parse("://example.com")).toThrow();
|
||||
expect(() => a.parse("http://")).toThrow();
|
||||
expect(() => a.parse("example.com")).toThrow();
|
||||
|
||||
// wrong type
|
||||
expect(() => a.parse(123)).toThrow();
|
||||
expect(() => a.parse(null)).toThrow();
|
||||
expect(() => a.parse(undefined)).toThrow();
|
||||
});
|
||||
|
||||
test("z.url with optional hostname regex", () => {
|
||||
const a = z.url({ hostname: /example\.com$/ });
|
||||
expect(a.parse("http://example.com")).toEqual("http://example.com");
|
||||
expect(a.parse("https://sub.example.com")).toEqual("https://sub.example.com");
|
||||
expect(() => a.parse("http://examples.com")).toThrow();
|
||||
expect(() => a.parse("http://example.org")).toThrow();
|
||||
expect(() => a.parse("asdf")).toThrow();
|
||||
});
|
||||
|
||||
test("z.url - file urls", () => {
|
||||
// file URLs
|
||||
const a = z.url({ hostname: /.*/ }); // allow any hostname
|
||||
expect(a.parse("file:///path/to/file.txt")).toEqual("file:///path/to/file.txt");
|
||||
expect(a.parse("file:///C:/path/to/file.txt")).toEqual("file:///C:/path/to/file.txt");
|
||||
expect(a.parse("file:///C:/path/to/file.txt?query=123#fragment")).toEqual(
|
||||
"file:///C:/path/to/file.txt?query=123#fragment"
|
||||
);
|
||||
});
|
||||
test("z.url with optional protocol regex", () => {
|
||||
const a = z.url({ protocol: /^https?$/ });
|
||||
expect(a.parse("http://example.com")).toEqual("http://example.com");
|
||||
expect(a.parse("https://example.com")).toEqual("https://example.com");
|
||||
expect(() => a.parse("ftp://example.com")).toThrow();
|
||||
expect(() => a.parse("mailto:example@example.com")).toThrow();
|
||||
expect(() => a.parse("asdf")).toThrow();
|
||||
});
|
||||
|
||||
test("z.url with both hostname and protocol regexes", () => {
|
||||
const a = z.url({ hostname: /example\.com$/, protocol: /^https$/ });
|
||||
expect(a.parse("https://example.com")).toEqual("https://example.com");
|
||||
expect(a.parse("https://sub.example.com")).toEqual("https://sub.example.com");
|
||||
expect(() => a.parse("http://example.com")).toThrow();
|
||||
expect(() => a.parse("https://example.org")).toThrow();
|
||||
expect(() => a.parse("ftp://example.com")).toThrow();
|
||||
expect(() => a.parse("asdf")).toThrow();
|
||||
});
|
||||
|
||||
test("z.url with invalid regex patterns", () => {
|
||||
const a = z.url({ hostname: /a+$/, protocol: /^ftp$/ });
|
||||
a.parse("ftp://a");
|
||||
a.parse("ftp://aaaaaaaa");
|
||||
expect(() => a.parse("http://aaa")).toThrow();
|
||||
expect(() => a.parse("https://example.com")).toThrow();
|
||||
expect(() => a.parse("ftp://asdfasdf")).toThrow();
|
||||
expect(() => a.parse("ftp://invalid")).toThrow();
|
||||
});
|
||||
|
||||
test("z.emoji", () => {
|
||||
const a = z.emoji();
|
||||
expect(z.parse(a, "😀")).toEqual("😀");
|
||||
expect(() => z.parse(a, "hello")).toThrow();
|
||||
});
|
||||
|
||||
test("z.nanoid", () => {
|
||||
const a = z.nanoid();
|
||||
expect(z.parse(a, "8FHZpIxleEK3axQRBNNjN")).toEqual("8FHZpIxleEK3axQRBNNjN");
|
||||
expect(() => z.parse(a, "abc")).toThrow();
|
||||
});
|
||||
|
||||
test("z.cuid", () => {
|
||||
const a = z.cuid();
|
||||
expect(z.parse(a, "cixs7y0c0000f7x3b1z6m3w6r")).toEqual("cixs7y0c0000f7x3b1z6m3w6r");
|
||||
expect(() => z.parse(a, "abc")).toThrow();
|
||||
});
|
||||
|
||||
test("z.cuid2", () => {
|
||||
const a = z.cuid2();
|
||||
expect(z.parse(a, "cixs7y0c0000f7x3b1z6m3w6r")).toEqual("cixs7y0c0000f7x3b1z6m3w6r");
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
});
|
||||
|
||||
test("z.ulid", () => {
|
||||
const a = z.ulid();
|
||||
expect(z.parse(a, "01ETGRM9QYVX6S9V2F3B6JXG4N")).toEqual("01ETGRM9QYVX6S9V2F3B6JXG4N");
|
||||
expect(() => z.parse(a, "abc")).toThrow();
|
||||
});
|
||||
|
||||
test("z.xid", () => {
|
||||
const a = z.xid();
|
||||
expect(z.parse(a, "9m4e2mr0ui3e8a215n4g")).toEqual("9m4e2mr0ui3e8a215n4g");
|
||||
expect(() => z.parse(a, "abc")).toThrow();
|
||||
});
|
||||
|
||||
test("z.ksuid", () => {
|
||||
const a = z.ksuid();
|
||||
expect(z.parse(a, "2naeRjTrrHJAkfd3tOuEjw90WCA")).toEqual("2naeRjTrrHJAkfd3tOuEjw90WCA");
|
||||
expect(() => z.parse(a, "abc")).toThrow();
|
||||
});
|
||||
|
||||
// test("z.ip", () => {
|
||||
// const a = z.ip();
|
||||
// expect(z.parse(a, "127.0.0.1")).toEqual("127.0.0.1");
|
||||
// expect(z.parse(a, "2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toEqual("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
|
||||
// expect(() => z.parse(a, "abc")).toThrow();
|
||||
// });
|
||||
|
||||
test("z.ipv4", () => {
|
||||
const a = z.ipv4();
|
||||
// valid ipv4
|
||||
expect(z.parse(a, "192.168.1.1")).toEqual("192.168.1.1");
|
||||
expect(z.parse(a, "255.255.255.255")).toEqual("255.255.255.255");
|
||||
// invalid ipv4
|
||||
expect(() => z.parse(a, "999.999.999.999")).toThrow();
|
||||
expect(() => z.parse(a, "256.256.256.256")).toThrow();
|
||||
expect(() => z.parse(a, "192.168.1")).toThrow();
|
||||
expect(() => z.parse(a, "hello")).toThrow();
|
||||
// wrong type
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
});
|
||||
|
||||
test("z.ipv6", () => {
|
||||
const a = z.ipv6();
|
||||
// valid ipv6
|
||||
expect(z.parse(a, "2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toEqual("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
|
||||
expect(z.parse(a, "::1")).toEqual("::1");
|
||||
// invalid ipv6
|
||||
expect(() => z.parse(a, "2001:db8::85a3::8a2e:370:7334")).toThrow();
|
||||
expect(() => z.parse(a, "2001:db8:85a3:0:0:8a2e:370g:7334")).toThrow();
|
||||
expect(() => z.parse(a, "hello")).toThrow();
|
||||
// wrong type
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
});
|
||||
|
||||
test("z.mac", () => {
|
||||
const a = z.mac();
|
||||
// valid mac
|
||||
expect(z.parse(a, "00:1A:2B:3C:4D:5E")).toEqual("00:1A:2B:3C:4D:5E");
|
||||
// invalid mac (dash delimiter not accepted by default)
|
||||
expect(() => z.parse(a, "01-23-45-67-89-AB")).toThrow();
|
||||
expect(() => z.parse(a, "00:1A:2B::4D:5E")).toThrow();
|
||||
expect(() => z.parse(a, "00:1a-2B:3c-4D:5e")).toThrow();
|
||||
expect(() => z.parse(a, "hello")).toThrow();
|
||||
// wrong type
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
});
|
||||
|
||||
test("z.mac with custom delimiter", () => {
|
||||
const a = z.mac({ delimiter: ":" });
|
||||
// valid mac with colon
|
||||
expect(z.parse(a, "00:1A:2B:3C:4D:5E")).toEqual("00:1A:2B:3C:4D:5E");
|
||||
// invalid mac with dash
|
||||
expect(() => z.parse(a, "00-1A-2B-3C-4D-5E")).toThrow();
|
||||
|
||||
const b = z.mac({ delimiter: "-" });
|
||||
// valid mac with dash
|
||||
expect(z.parse(b, "00-1A-2B-3C-4D-5E")).toEqual("00-1A-2B-3C-4D-5E");
|
||||
// invalid mac with colon
|
||||
expect(() => z.parse(b, "00:1A:2B:3C:4D:5E")).toThrow();
|
||||
|
||||
const c = z.mac({ delimiter: ":" });
|
||||
// colon-only mac
|
||||
expect(z.parse(c, "00:1A:2B:3C:4D:5E")).toEqual("00:1A:2B:3C:4D:5E");
|
||||
expect(() => z.parse(c, "00-1A-2B-3C-4D-5E")).toThrow();
|
||||
});
|
||||
|
||||
test("z.base64", () => {
|
||||
const a = z.base64();
|
||||
// valid base64
|
||||
expect(z.parse(a, "SGVsbG8gd29ybGQ=")).toEqual("SGVsbG8gd29ybGQ=");
|
||||
expect(z.parse(a, "U29tZSBvdGhlciBzdHJpbmc=")).toEqual("U29tZSBvdGhlciBzdHJpbmc=");
|
||||
// invalid base64
|
||||
expect(() => z.parse(a, "SGVsbG8gd29ybGQ")).toThrow();
|
||||
expect(() => z.parse(a, "U29tZSBvdGhlciBzdHJpbmc")).toThrow();
|
||||
expect(() => z.parse(a, "hello")).toThrow();
|
||||
// whitespace is not allowed (atob would otherwise strip it)
|
||||
expect(() => z.parse(a, "123 ")).toThrow();
|
||||
expect(() => z.parse(a, "SGVsbG8gd29ybGQ= ")).toThrow();
|
||||
expect(() => z.parse(a, "SGVsbG8gd29ybGQ=\n")).toThrow();
|
||||
expect(() => z.parse(a, "SGVs bG8gd29ybGQ=")).toThrow();
|
||||
// wrong type
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
});
|
||||
|
||||
// test("z.jsonString", () => {
|
||||
// const a = z.jsonString();
|
||||
// // valid JSON string
|
||||
// expect(z.parse(a, '{"key":"value"}')).toEqual('{"key":"value"}');
|
||||
// expect(z.parse(a, '["item1", "item2"]')).toEqual('["item1", "item2"]');
|
||||
// // invalid JSON string
|
||||
// expect(() => z.parse(a, '{"key":value}')).toThrow();
|
||||
// expect(() => z.parse(a, '["item1", "item2"')).toThrow();
|
||||
// expect(() => z.parse(a, "hello")).toThrow();
|
||||
// // wrong type
|
||||
// expect(() => z.parse(a, 123)).toThrow();
|
||||
// });
|
||||
|
||||
test("z.e164", () => {
|
||||
const a = z.e164();
|
||||
// valid e164
|
||||
expect(z.parse(a, "+1234567890")).toEqual("+1234567890");
|
||||
expect(z.parse(a, "+19876543210")).toEqual("+19876543210");
|
||||
// invalid e164
|
||||
expect(() => z.parse(a, "1234567890")).toThrow();
|
||||
expect(() => z.parse(a, "+12345")).toThrow();
|
||||
expect(() => z.parse(a, "hello")).toThrow();
|
||||
// wrong type
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
});
|
||||
|
||||
test("z.jwt", () => {
|
||||
const a = z.jwt();
|
||||
// valid jwt
|
||||
expect(
|
||||
z.parse(
|
||||
a,
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
)
|
||||
).toEqual(
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
);
|
||||
// invalid jwt
|
||||
expect(() => z.parse(a, "invalid.jwt.token")).toThrow();
|
||||
expect(() => z.parse(a, "hello")).toThrow();
|
||||
// wrong type
|
||||
expect(() => z.parse(a, 123)).toThrow();
|
||||
});
|
||||
|
||||
test("z.hash generic format", () => {
|
||||
expect(z.hash("sha256").parse("a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3")).toBe(
|
||||
"a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"
|
||||
);
|
||||
|
||||
// --- Type-level checks (ensure the literal format string is encoded in the return type)
|
||||
expectTypeOf(z.hash("md5")).toEqualTypeOf<z.ZodMiniCustomStringFormat<"md5_hex">>();
|
||||
expectTypeOf(z.hash("sha1")).toEqualTypeOf<z.ZodMiniCustomStringFormat<"sha1_hex">>();
|
||||
expectTypeOf(z.hash("sha256", { enc: "base64" as const })).toEqualTypeOf<
|
||||
z.ZodMiniCustomStringFormat<"sha256_base64">
|
||||
>();
|
||||
expectTypeOf(z.hash("sha384", { enc: "base64url" as const })).toEqualTypeOf<
|
||||
z.ZodMiniCustomStringFormat<"sha384_base64url">
|
||||
>();
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,128 @@
|
||||
"use strict";
|
||||
// THIS CODE WAS AUTOMATICALLY GENERATED
|
||||
// DO NOT EDIT THIS CODE BY HAND
|
||||
// SEE https://typescript-eslint.io/users/configs
|
||||
//
|
||||
// For developers working in the typescript-eslint monorepo:
|
||||
// You can regenerate it using `pnpm run generate-configs`
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const base_1 = __importDefault(require("./base"));
|
||||
const eslint_recommended_1 = __importDefault(require("./eslint-recommended"));
|
||||
/**
|
||||
* Contains all of `recommended`, `recommended-type-checked`, and `strict`, along with additional strict rules that require type information.
|
||||
* @see {@link https://typescript-eslint.io/users/configs#strict-type-checked}
|
||||
*/
|
||||
exports.default = (plugin, parser) => [
|
||||
(0, base_1.default)(plugin, parser),
|
||||
(0, eslint_recommended_1.default)(plugin, parser),
|
||||
{
|
||||
name: 'typescript-eslint/strict-type-checked',
|
||||
rules: {
|
||||
'@typescript-eslint/await-thenable': 'error',
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{ minimumDescriptionLength: 10 },
|
||||
],
|
||||
'no-array-constructor': 'off',
|
||||
'@typescript-eslint/no-array-constructor': 'error',
|
||||
'@typescript-eslint/no-array-delete': 'error',
|
||||
'@typescript-eslint/no-base-to-string': 'error',
|
||||
'@typescript-eslint/no-confusing-void-expression': 'error',
|
||||
'@typescript-eslint/no-deprecated': 'error',
|
||||
'@typescript-eslint/no-duplicate-enum-values': 'error',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'error',
|
||||
'@typescript-eslint/no-dynamic-delete': 'error',
|
||||
'@typescript-eslint/no-empty-object-type': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
'@typescript-eslint/no-extra-non-null-assertion': 'error',
|
||||
'@typescript-eslint/no-extraneous-class': 'error',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-for-in-array': 'error',
|
||||
'no-implied-eval': 'off',
|
||||
'@typescript-eslint/no-implied-eval': 'error',
|
||||
'@typescript-eslint/no-invalid-void-type': 'error',
|
||||
'@typescript-eslint/no-meaningless-void-operator': 'error',
|
||||
'@typescript-eslint/no-misused-new': 'error',
|
||||
'@typescript-eslint/no-misused-promises': 'error',
|
||||
'@typescript-eslint/no-misused-spread': 'error',
|
||||
'@typescript-eslint/no-mixed-enums': 'error',
|
||||
'@typescript-eslint/no-namespace': 'error',
|
||||
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error',
|
||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'error',
|
||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'error',
|
||||
'@typescript-eslint/no-require-imports': 'error',
|
||||
'@typescript-eslint/no-this-alias': 'error',
|
||||
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error',
|
||||
'@typescript-eslint/no-unnecessary-condition': 'error',
|
||||
'@typescript-eslint/no-unnecessary-template-expression': 'error',
|
||||
'@typescript-eslint/no-unnecessary-type-arguments': 'error',
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
|
||||
'@typescript-eslint/no-unnecessary-type-constraint': 'error',
|
||||
'@typescript-eslint/no-unnecessary-type-conversion': 'error',
|
||||
'@typescript-eslint/no-unnecessary-type-parameters': 'error',
|
||||
'@typescript-eslint/no-unsafe-argument': 'error',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'error',
|
||||
'@typescript-eslint/no-unsafe-call': 'error',
|
||||
'@typescript-eslint/no-unsafe-declaration-merging': 'error',
|
||||
'@typescript-eslint/no-unsafe-enum-comparison': 'error',
|
||||
'@typescript-eslint/no-unsafe-function-type': 'error',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'error',
|
||||
'@typescript-eslint/no-unsafe-return': 'error',
|
||||
'@typescript-eslint/no-unsafe-unary-minus': 'error',
|
||||
'no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'error',
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'no-useless-constructor': 'off',
|
||||
'@typescript-eslint/no-useless-constructor': 'error',
|
||||
'@typescript-eslint/no-useless-default-assignment': 'error',
|
||||
'@typescript-eslint/no-wrapper-object-types': 'error',
|
||||
'no-throw-literal': 'off',
|
||||
'@typescript-eslint/only-throw-error': 'error',
|
||||
'@typescript-eslint/prefer-as-const': 'error',
|
||||
'@typescript-eslint/prefer-literal-enum-member': 'error',
|
||||
'@typescript-eslint/prefer-namespace-keyword': 'error',
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
'@typescript-eslint/prefer-promise-reject-errors': 'error',
|
||||
'@typescript-eslint/prefer-reduce-type-parameter': 'error',
|
||||
'@typescript-eslint/prefer-return-this-type': 'error',
|
||||
'@typescript-eslint/related-getter-setter-pairs': 'error',
|
||||
'require-await': 'off',
|
||||
'@typescript-eslint/require-await': 'error',
|
||||
'@typescript-eslint/restrict-plus-operands': [
|
||||
'error',
|
||||
{
|
||||
allowAny: false,
|
||||
allowBoolean: false,
|
||||
allowNullish: false,
|
||||
allowNumberAndString: false,
|
||||
allowRegExp: false,
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/restrict-template-expressions': [
|
||||
'error',
|
||||
{
|
||||
allowAny: false,
|
||||
allowBoolean: false,
|
||||
allowNever: false,
|
||||
allowNullish: false,
|
||||
allowNumber: false,
|
||||
allowRegExp: false,
|
||||
},
|
||||
],
|
||||
'no-return-await': 'off',
|
||||
'@typescript-eslint/return-await': [
|
||||
'error',
|
||||
'error-handling-correctness-only',
|
||||
],
|
||||
'@typescript-eslint/triple-slash-reference': 'error',
|
||||
'@typescript-eslint/unbound-method': 'error',
|
||||
'@typescript-eslint/unified-signatures': 'error',
|
||||
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
|
||||
exports._ = require("tslib").__addDisposableResource;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noExtraNonNullAssertion", [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
||||
name: string;
|
||||
};
|
||||
export default _default;
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"wtf8.js","sourceRoot":"","sources":["../../../src/api/node/wtf8.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAC/B,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AACjC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAGjC,SAAS,eAAe,CAAC,KAAiB,EAAE,KAAa;IACrD,OAAO,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM;WACxB,KAAK,CAAC,KAAK,CAAC,KAAK,iBAAiB;WAClC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,sBAAsB;WAC1C,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,sBAAsB;WAC1C,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,mBAAmB;WACvC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,mBAAmB,CAAC;AACnD,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAiB,EAAE,KAAa;IAC1D,OAAO,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAiB;IAC3C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;AACzG,CAAC;AAED,SAAS,YAAY,CAAC,KAAqC;IACvD,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,OAAO,WAAY,SAAQ,WAAW;IAC/B,MAAM,CAAC,KAAsC,EAAE,OAAuB;QAC3E,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;gBAC7B,SAAS;YACb,CAAC;YAED,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YACvE,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,oBAAoB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAChE,CAAC,IAAI,CAAC,CAAC;YACP,YAAY,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAED,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,YAAY,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;CACJ"}
|
||||
@@ -0,0 +1,833 @@
|
||||
"use strict";
|
||||
// This rule was feature-frozen before we enabled no-property-in-node.
|
||||
/* eslint-disable eslint-plugin/no-property-in-node */
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.defaultOrder = void 0;
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
const natural_compare_1 = __importDefault(require("natural-compare"));
|
||||
const util_1 = require("../util");
|
||||
const neverConfig = {
|
||||
type: 'string',
|
||||
enum: ['never'],
|
||||
};
|
||||
const arrayConfig = (memberTypes) => ({
|
||||
type: 'array',
|
||||
items: {
|
||||
oneOf: [
|
||||
{
|
||||
$ref: memberTypes,
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: memberTypes,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const objectConfig = (memberTypes) => ({
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
memberTypes: {
|
||||
oneOf: [arrayConfig(memberTypes), neverConfig],
|
||||
},
|
||||
optionalityOrder: {
|
||||
$ref: '#/items/0/$defs/optionalityOrderOptions',
|
||||
},
|
||||
order: {
|
||||
$ref: '#/items/0/$defs/orderOptions',
|
||||
},
|
||||
},
|
||||
});
|
||||
exports.defaultOrder = [
|
||||
// Index signature
|
||||
'signature',
|
||||
'call-signature',
|
||||
// Fields
|
||||
'public-static-field',
|
||||
'protected-static-field',
|
||||
'private-static-field',
|
||||
'#private-static-field',
|
||||
'public-decorated-field',
|
||||
'protected-decorated-field',
|
||||
'private-decorated-field',
|
||||
'public-instance-field',
|
||||
'protected-instance-field',
|
||||
'private-instance-field',
|
||||
'#private-instance-field',
|
||||
'public-abstract-field',
|
||||
'protected-abstract-field',
|
||||
'public-field',
|
||||
'protected-field',
|
||||
'private-field',
|
||||
'#private-field',
|
||||
'static-field',
|
||||
'instance-field',
|
||||
'abstract-field',
|
||||
'decorated-field',
|
||||
'field',
|
||||
// Static initialization
|
||||
'static-initialization',
|
||||
// Constructors
|
||||
'public-constructor',
|
||||
'protected-constructor',
|
||||
'private-constructor',
|
||||
'constructor',
|
||||
// Accessors
|
||||
'public-static-accessor',
|
||||
'protected-static-accessor',
|
||||
'private-static-accessor',
|
||||
'#private-static-accessor',
|
||||
'public-decorated-accessor',
|
||||
'protected-decorated-accessor',
|
||||
'private-decorated-accessor',
|
||||
'public-instance-accessor',
|
||||
'protected-instance-accessor',
|
||||
'private-instance-accessor',
|
||||
'#private-instance-accessor',
|
||||
'public-abstract-accessor',
|
||||
'protected-abstract-accessor',
|
||||
'public-accessor',
|
||||
'protected-accessor',
|
||||
'private-accessor',
|
||||
'#private-accessor',
|
||||
'static-accessor',
|
||||
'instance-accessor',
|
||||
'abstract-accessor',
|
||||
'decorated-accessor',
|
||||
'accessor',
|
||||
// Getters
|
||||
'public-static-get',
|
||||
'protected-static-get',
|
||||
'private-static-get',
|
||||
'#private-static-get',
|
||||
'public-decorated-get',
|
||||
'protected-decorated-get',
|
||||
'private-decorated-get',
|
||||
'public-instance-get',
|
||||
'protected-instance-get',
|
||||
'private-instance-get',
|
||||
'#private-instance-get',
|
||||
'public-abstract-get',
|
||||
'protected-abstract-get',
|
||||
'public-get',
|
||||
'protected-get',
|
||||
'private-get',
|
||||
'#private-get',
|
||||
'static-get',
|
||||
'instance-get',
|
||||
'abstract-get',
|
||||
'decorated-get',
|
||||
'get',
|
||||
// Setters
|
||||
'public-static-set',
|
||||
'protected-static-set',
|
||||
'private-static-set',
|
||||
'#private-static-set',
|
||||
'public-decorated-set',
|
||||
'protected-decorated-set',
|
||||
'private-decorated-set',
|
||||
'public-instance-set',
|
||||
'protected-instance-set',
|
||||
'private-instance-set',
|
||||
'#private-instance-set',
|
||||
'public-abstract-set',
|
||||
'protected-abstract-set',
|
||||
'public-set',
|
||||
'protected-set',
|
||||
'private-set',
|
||||
'#private-set',
|
||||
'static-set',
|
||||
'instance-set',
|
||||
'abstract-set',
|
||||
'decorated-set',
|
||||
'set',
|
||||
// Methods
|
||||
'public-static-method',
|
||||
'protected-static-method',
|
||||
'private-static-method',
|
||||
'#private-static-method',
|
||||
'public-decorated-method',
|
||||
'protected-decorated-method',
|
||||
'private-decorated-method',
|
||||
'public-instance-method',
|
||||
'protected-instance-method',
|
||||
'private-instance-method',
|
||||
'#private-instance-method',
|
||||
'public-abstract-method',
|
||||
'protected-abstract-method',
|
||||
'public-method',
|
||||
'protected-method',
|
||||
'private-method',
|
||||
'#private-method',
|
||||
'static-method',
|
||||
'instance-method',
|
||||
'abstract-method',
|
||||
'decorated-method',
|
||||
'method',
|
||||
];
|
||||
const allMemberTypes = [
|
||||
...new Set([
|
||||
'readonly-signature',
|
||||
'signature',
|
||||
'readonly-field',
|
||||
'field',
|
||||
'method',
|
||||
'call-signature',
|
||||
'constructor',
|
||||
'accessor',
|
||||
'get',
|
||||
'set',
|
||||
'static-initialization',
|
||||
].flatMap(type => [
|
||||
type,
|
||||
...['public', 'protected', 'private', '#private']
|
||||
.flatMap(accessibility => [
|
||||
type !== 'readonly-signature' &&
|
||||
type !== 'signature' &&
|
||||
type !== 'static-initialization' &&
|
||||
type !== 'call-signature' &&
|
||||
!(type === 'constructor' && accessibility === '#private')
|
||||
? `${accessibility}-${type}` // e.g. `public-field`
|
||||
: [],
|
||||
// Only class instance fields, methods, accessors, get and set can have decorators attached to them
|
||||
accessibility !== '#private' &&
|
||||
(type === 'readonly-field' ||
|
||||
type === 'field' ||
|
||||
type === 'method' ||
|
||||
type === 'accessor' ||
|
||||
type === 'get' ||
|
||||
type === 'set')
|
||||
? [`${accessibility}-decorated-${type}`, `decorated-${type}`]
|
||||
: [],
|
||||
type !== 'constructor' &&
|
||||
type !== 'readonly-signature' &&
|
||||
type !== 'signature' &&
|
||||
type !== 'call-signature'
|
||||
? [
|
||||
'static',
|
||||
'instance',
|
||||
// There is no `static-constructor` or `instance-constructor` or `abstract-constructor`
|
||||
...(accessibility === '#private' ||
|
||||
accessibility === 'private'
|
||||
? []
|
||||
: ['abstract']),
|
||||
].flatMap(scope => [
|
||||
`${scope}-${type}`,
|
||||
`${accessibility}-${scope}-${type}`,
|
||||
])
|
||||
: [],
|
||||
])
|
||||
.flat(),
|
||||
])),
|
||||
];
|
||||
const functionExpressions = [
|
||||
utils_1.AST_NODE_TYPES.FunctionExpression,
|
||||
utils_1.AST_NODE_TYPES.ArrowFunctionExpression,
|
||||
];
|
||||
/**
|
||||
* Gets the node type.
|
||||
*
|
||||
* @param node the node to be evaluated.
|
||||
*/
|
||||
function getNodeType(node) {
|
||||
switch (node.type) {
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
case utils_1.AST_NODE_TYPES.MethodDefinition:
|
||||
case utils_1.AST_NODE_TYPES.TSMethodSignature:
|
||||
return node.kind;
|
||||
case utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration:
|
||||
return 'call-signature';
|
||||
case utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration:
|
||||
return 'constructor';
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
||||
case utils_1.AST_NODE_TYPES.TSPropertySignature:
|
||||
return node.readonly ? 'readonly-field' : 'field';
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty:
|
||||
case utils_1.AST_NODE_TYPES.AccessorProperty:
|
||||
return 'accessor';
|
||||
case utils_1.AST_NODE_TYPES.PropertyDefinition:
|
||||
return node.value && functionExpressions.includes(node.value.type)
|
||||
? 'method'
|
||||
: node.readonly
|
||||
? 'readonly-field'
|
||||
: 'field';
|
||||
case utils_1.AST_NODE_TYPES.TSIndexSignature:
|
||||
return node.readonly ? 'readonly-signature' : 'signature';
|
||||
case utils_1.AST_NODE_TYPES.StaticBlock:
|
||||
return 'static-initialization';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets the raw string value of a member's name
|
||||
*/
|
||||
function getMemberRawName(member, sourceCode) {
|
||||
const { name, type } = (0, util_1.getNameFromMember)(member, sourceCode);
|
||||
if (type === util_1.MemberNameType.Quoted) {
|
||||
return name.slice(1, -1);
|
||||
}
|
||||
if (type === util_1.MemberNameType.Private) {
|
||||
return name.slice(1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
/**
|
||||
* Gets the member name based on the member type.
|
||||
*
|
||||
* @param node the node to be evaluated.
|
||||
*/
|
||||
function getMemberName(node, sourceCode) {
|
||||
switch (node.type) {
|
||||
case utils_1.AST_NODE_TYPES.TSPropertySignature:
|
||||
case utils_1.AST_NODE_TYPES.TSMethodSignature:
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty:
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
||||
case utils_1.AST_NODE_TYPES.AccessorProperty:
|
||||
case utils_1.AST_NODE_TYPES.PropertyDefinition:
|
||||
return getMemberRawName(node, sourceCode);
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
case utils_1.AST_NODE_TYPES.MethodDefinition:
|
||||
return node.kind === 'constructor'
|
||||
? 'constructor'
|
||||
: getMemberRawName(node, sourceCode);
|
||||
case utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration:
|
||||
return 'new';
|
||||
case utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration:
|
||||
return 'call';
|
||||
case utils_1.AST_NODE_TYPES.TSIndexSignature:
|
||||
return (0, util_1.getNameFromIndexSignature)(node);
|
||||
case utils_1.AST_NODE_TYPES.StaticBlock:
|
||||
return 'static block';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns true if the member is optional based on the member type.
|
||||
*
|
||||
* @param node the node to be evaluated.
|
||||
*
|
||||
* @returns Whether the member is optional, or false if it cannot be optional at all.
|
||||
*/
|
||||
function isMemberOptional(node) {
|
||||
switch (node.type) {
|
||||
case utils_1.AST_NODE_TYPES.TSPropertySignature:
|
||||
case utils_1.AST_NODE_TYPES.TSMethodSignature:
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty:
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
||||
case utils_1.AST_NODE_TYPES.AccessorProperty:
|
||||
case utils_1.AST_NODE_TYPES.PropertyDefinition:
|
||||
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
case utils_1.AST_NODE_TYPES.MethodDefinition:
|
||||
return node.optional;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Gets the calculated rank using the provided method definition.
|
||||
* The algorithm is as follows:
|
||||
* - Get the rank based on the accessibility-scope-type name, e.g. public-instance-field
|
||||
* - If there is no order for accessibility-scope-type, then strip out the accessibility.
|
||||
* - If there is no order for scope-type, then strip out the scope.
|
||||
* - If there is no order for type, then return -1
|
||||
* @param memberGroups the valid names to be validated.
|
||||
* @param orderConfig the current order to be validated.
|
||||
*
|
||||
* @return Index of the matching member type in the order configuration.
|
||||
*/
|
||||
function getRankOrder(memberGroups, orderConfig) {
|
||||
let rank = -1;
|
||||
const stack = [...memberGroups]; // Get a copy of the member groups
|
||||
while (stack.length > 0 && rank === -1) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const memberGroup = stack.shift();
|
||||
rank = orderConfig.findIndex(memberType => Array.isArray(memberType)
|
||||
? memberType.includes(memberGroup)
|
||||
: memberType === memberGroup);
|
||||
}
|
||||
return rank;
|
||||
}
|
||||
function getAccessibility(node) {
|
||||
if ('accessibility' in node && node.accessibility) {
|
||||
return node.accessibility;
|
||||
}
|
||||
if ('key' in node && node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
|
||||
return '#private';
|
||||
}
|
||||
return 'public';
|
||||
}
|
||||
/**
|
||||
* Gets the rank of the node given the order.
|
||||
* @param node the node to be evaluated.
|
||||
* @param orderConfig the current order to be validated.
|
||||
* @param supportsModifiers a flag indicating whether the type supports modifiers (scope or accessibility) or not.
|
||||
*/
|
||||
function getRank(node, orderConfig, supportsModifiers) {
|
||||
const type = getNodeType(node);
|
||||
if (node.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
||||
node.value.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) {
|
||||
return -1;
|
||||
}
|
||||
if (type == null) {
|
||||
// shouldn't happen but just in case, put it on the end
|
||||
return orderConfig.length - 1;
|
||||
}
|
||||
const abstract = node.type === utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty ||
|
||||
node.type === utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition ||
|
||||
node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition;
|
||||
const scope = 'static' in node && node.static
|
||||
? 'static'
|
||||
: abstract
|
||||
? 'abstract'
|
||||
: 'instance';
|
||||
const accessibility = getAccessibility(node);
|
||||
// Collect all existing member groups that apply to this node...
|
||||
// (e.g. 'public-instance-field', 'instance-field', 'public-field', 'constructor' etc.)
|
||||
const memberGroups = [];
|
||||
if (supportsModifiers) {
|
||||
const decorated = 'decorators' in node && node.decorators.length > 0;
|
||||
if (decorated &&
|
||||
(type === 'readonly-field' ||
|
||||
type === 'field' ||
|
||||
type === 'method' ||
|
||||
type === 'accessor' ||
|
||||
type === 'get' ||
|
||||
type === 'set')) {
|
||||
memberGroups.push(`${accessibility}-decorated-${type}`);
|
||||
memberGroups.push(`decorated-${type}`);
|
||||
if (type === 'readonly-field') {
|
||||
memberGroups.push(`${accessibility}-decorated-field`);
|
||||
memberGroups.push(`decorated-field`);
|
||||
}
|
||||
}
|
||||
if (type !== 'readonly-signature' &&
|
||||
type !== 'signature' &&
|
||||
type !== 'static-initialization') {
|
||||
if (type !== 'constructor') {
|
||||
// Constructors have no scope
|
||||
memberGroups.push(`${accessibility}-${scope}-${type}`);
|
||||
memberGroups.push(`${scope}-${type}`);
|
||||
if (type === 'readonly-field') {
|
||||
memberGroups.push(`${accessibility}-${scope}-field`);
|
||||
memberGroups.push(`${scope}-field`);
|
||||
}
|
||||
}
|
||||
memberGroups.push(`${accessibility}-${type}`);
|
||||
if (type === 'readonly-field') {
|
||||
memberGroups.push(`${accessibility}-field`);
|
||||
}
|
||||
}
|
||||
}
|
||||
memberGroups.push(type);
|
||||
if (type === 'readonly-signature') {
|
||||
memberGroups.push('signature');
|
||||
}
|
||||
else if (type === 'readonly-field') {
|
||||
memberGroups.push('field');
|
||||
}
|
||||
// ...then get the rank order for those member groups based on the node
|
||||
return getRankOrder(memberGroups, orderConfig);
|
||||
}
|
||||
/**
|
||||
* Groups members into arrays of consecutive members with the same rank.
|
||||
* If, for example, the memberSet parameter looks like the following...
|
||||
* @example
|
||||
* ```
|
||||
* interface Foo {
|
||||
* [a: string]: number;
|
||||
*
|
||||
* a: x;
|
||||
* B: x;
|
||||
* c: x;
|
||||
*
|
||||
* c(): void;
|
||||
* B(): void;
|
||||
* a(): void;
|
||||
*
|
||||
* (): Baz;
|
||||
*
|
||||
* new (): Bar;
|
||||
* }
|
||||
* ```
|
||||
* ...the resulting array will look like: [[a, B, c], [c, B, a]].
|
||||
* @param memberSet The members to be grouped.
|
||||
* @param memberType The configured order of member types.
|
||||
* @param supportsModifiers It'll get passed to getRank().
|
||||
* @returns The array of groups of members.
|
||||
*/
|
||||
function groupMembersByType(members, memberTypes, supportsModifiers) {
|
||||
const groupedMembers = [];
|
||||
const memberRanks = members.map(member => getRank(member, memberTypes, supportsModifiers));
|
||||
let previousRank = undefined;
|
||||
members.forEach((member, index) => {
|
||||
if (index === members.length - 1) {
|
||||
return;
|
||||
}
|
||||
const rankOfCurrentMember = memberRanks[index];
|
||||
const rankOfNextMember = memberRanks[index + 1];
|
||||
if (rankOfCurrentMember === previousRank) {
|
||||
groupedMembers.at(-1)?.push(member);
|
||||
}
|
||||
else if (rankOfCurrentMember === rankOfNextMember) {
|
||||
groupedMembers.push([member]);
|
||||
previousRank = rankOfCurrentMember;
|
||||
}
|
||||
});
|
||||
return groupedMembers;
|
||||
}
|
||||
/**
|
||||
* Gets the lowest possible rank(s) higher than target.
|
||||
* e.g. given the following order:
|
||||
* ...
|
||||
* public-static-method
|
||||
* protected-static-method
|
||||
* private-static-method
|
||||
* public-instance-method
|
||||
* protected-instance-method
|
||||
* private-instance-method
|
||||
* ...
|
||||
* and considering that a public-instance-method has already been declared, so ranks contains
|
||||
* public-instance-method, then the lowest possible rank for public-static-method is
|
||||
* public-instance-method.
|
||||
* If a lowest possible rank is a member group, a comma separated list of ranks is returned.
|
||||
* @param ranks the existing ranks in the object.
|
||||
* @param target the minimum target rank to filter on.
|
||||
* @param order the current order to be validated.
|
||||
* @returns the name(s) of the lowest possible rank without dashes (-).
|
||||
*/
|
||||
function getLowestRank(ranks, target, order) {
|
||||
let lowest = ranks[ranks.length - 1];
|
||||
ranks.forEach(rank => {
|
||||
if (rank > target) {
|
||||
lowest = Math.min(lowest, rank);
|
||||
}
|
||||
});
|
||||
const lowestRank = order[lowest];
|
||||
const lowestRanks = Array.isArray(lowestRank) ? lowestRank : [lowestRank];
|
||||
return lowestRanks.map(rank => rank.replaceAll('-', ' ')).join(', ');
|
||||
}
|
||||
exports.default = (0, util_1.createRule)({
|
||||
name: 'member-ordering',
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Require a consistent member declaration order',
|
||||
frozen: true,
|
||||
},
|
||||
messages: {
|
||||
incorrectGroupOrder: 'Member {{name}} should be declared before all {{rank}} definitions.',
|
||||
incorrectOrder: 'Member {{member}} should be declared before member {{beforeMember}}.',
|
||||
incorrectRequiredMembersOrder: `Member {{member}} should be declared after all {{optionalOrRequired}} members.`,
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
$defs: {
|
||||
allItems: {
|
||||
type: 'string',
|
||||
enum: allMemberTypes,
|
||||
},
|
||||
optionalityOrderOptions: {
|
||||
type: 'string',
|
||||
enum: ['optional-first', 'required-first'],
|
||||
},
|
||||
orderOptions: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'alphabetically',
|
||||
'alphabetically-case-insensitive',
|
||||
'as-written',
|
||||
'natural',
|
||||
'natural-case-insensitive',
|
||||
],
|
||||
},
|
||||
typeItems: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'readonly-signature',
|
||||
'signature',
|
||||
'readonly-field',
|
||||
'field',
|
||||
'method',
|
||||
'constructor',
|
||||
],
|
||||
},
|
||||
// ajv is order-dependent; these configs must come last
|
||||
baseConfig: {
|
||||
oneOf: [
|
||||
neverConfig,
|
||||
arrayConfig('#/items/0/$defs/allItems'),
|
||||
objectConfig('#/items/0/$defs/allItems'),
|
||||
],
|
||||
},
|
||||
typesConfig: {
|
||||
oneOf: [
|
||||
neverConfig,
|
||||
arrayConfig('#/items/0/$defs/typeItems'),
|
||||
objectConfig('#/items/0/$defs/typeItems'),
|
||||
],
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
classes: {
|
||||
$ref: '#/items/0/$defs/baseConfig',
|
||||
description: 'Which ordering to enforce for classes.',
|
||||
},
|
||||
classExpressions: {
|
||||
$ref: '#/items/0/$defs/baseConfig',
|
||||
description: 'Which ordering to enforce for classExpressions.',
|
||||
},
|
||||
default: {
|
||||
$ref: '#/items/0/$defs/baseConfig',
|
||||
description: 'Which ordering to enforce for default.',
|
||||
},
|
||||
interfaces: {
|
||||
$ref: '#/items/0/$defs/typesConfig',
|
||||
description: 'Which ordering to enforce for interfaces.',
|
||||
},
|
||||
typeLiterals: {
|
||||
$ref: '#/items/0/$defs/typesConfig',
|
||||
description: 'Which ordering to enforce for typeLiterals.',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultOptions: [
|
||||
{
|
||||
default: {
|
||||
memberTypes: exports.defaultOrder,
|
||||
},
|
||||
},
|
||||
],
|
||||
create(context, [options]) {
|
||||
/**
|
||||
* Checks if the member groups are correctly sorted.
|
||||
*
|
||||
* @param members Members to be validated.
|
||||
* @param groupOrder Group order to be validated.
|
||||
* @param supportsModifiers A flag indicating whether the type supports modifiers (scope or accessibility) or not.
|
||||
*
|
||||
* @return Array of member groups or null if one of the groups is not correctly sorted.
|
||||
*/
|
||||
function checkGroupSort(members, groupOrder, supportsModifiers) {
|
||||
const previousRanks = [];
|
||||
const memberGroups = [];
|
||||
let isCorrectlySorted = true;
|
||||
// Find first member which isn't correctly sorted
|
||||
for (const member of members) {
|
||||
const rank = getRank(member, groupOrder, supportsModifiers);
|
||||
const name = getMemberName(member, context.sourceCode);
|
||||
const rankLastMember = previousRanks[previousRanks.length - 1];
|
||||
if (rank === -1) {
|
||||
continue;
|
||||
}
|
||||
// Works for 1st item because x < undefined === false for any x (typeof string)
|
||||
if (rank < rankLastMember) {
|
||||
context.report({
|
||||
node: member,
|
||||
messageId: 'incorrectGroupOrder',
|
||||
data: {
|
||||
name,
|
||||
rank: getLowestRank(previousRanks, rank, groupOrder),
|
||||
},
|
||||
});
|
||||
isCorrectlySorted = false;
|
||||
}
|
||||
else if (rank === rankLastMember) {
|
||||
// Same member group --> Push to existing member group array
|
||||
memberGroups[memberGroups.length - 1].push(member);
|
||||
}
|
||||
else {
|
||||
// New member group --> Create new member group array
|
||||
previousRanks.push(rank);
|
||||
memberGroups.push([member]);
|
||||
}
|
||||
}
|
||||
return isCorrectlySorted ? memberGroups : null;
|
||||
}
|
||||
/**
|
||||
* Checks if the members are alphabetically sorted.
|
||||
*
|
||||
* @param members Members to be validated.
|
||||
* @param order What order the members should be sorted in.
|
||||
*
|
||||
* @return True if all members are correctly sorted.
|
||||
*/
|
||||
function checkAlphaSort(members, order) {
|
||||
let previousName = '';
|
||||
let isCorrectlySorted = true;
|
||||
// Find first member which isn't correctly sorted
|
||||
members.forEach(member => {
|
||||
const name = getMemberName(member, context.sourceCode);
|
||||
// Note: Not all members have names
|
||||
if (name) {
|
||||
if (naturalOutOfOrder(name, previousName, order)) {
|
||||
context.report({
|
||||
node: member,
|
||||
messageId: 'incorrectOrder',
|
||||
data: {
|
||||
beforeMember: previousName,
|
||||
member: name,
|
||||
},
|
||||
});
|
||||
isCorrectlySorted = false;
|
||||
}
|
||||
previousName = name;
|
||||
}
|
||||
});
|
||||
return isCorrectlySorted;
|
||||
}
|
||||
function naturalOutOfOrder(name, previousName, order) {
|
||||
if (name === previousName) {
|
||||
return false;
|
||||
}
|
||||
switch (order) {
|
||||
case 'alphabetically':
|
||||
return name < previousName;
|
||||
case 'alphabetically-case-insensitive':
|
||||
return name.toLowerCase() < previousName.toLowerCase();
|
||||
case 'natural':
|
||||
return (0, natural_compare_1.default)(name, previousName) !== 1;
|
||||
case 'natural-case-insensitive':
|
||||
return ((0, natural_compare_1.default)(name.toLowerCase(), previousName.toLowerCase()) !== 1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Checks if the order of optional and required members is correct based
|
||||
* on the given 'required' parameter.
|
||||
*
|
||||
* @param members Members to be validated.
|
||||
* @param optionalityOrder Where to place optional members, if not intermixed.
|
||||
*
|
||||
* @return True if all required and optional members are correctly sorted.
|
||||
*/
|
||||
function checkRequiredOrder(members, optionalityOrder) {
|
||||
const switchIndex = members.findIndex((member, i) => i && isMemberOptional(member) !== isMemberOptional(members[i - 1]));
|
||||
const report = (member) => context.report({
|
||||
loc: member.loc,
|
||||
messageId: 'incorrectRequiredMembersOrder',
|
||||
data: {
|
||||
member: getMemberName(member, context.sourceCode),
|
||||
optionalOrRequired: optionalityOrder === 'required-first' ? 'required' : 'optional',
|
||||
},
|
||||
});
|
||||
// if the optionality of the first item is correct (based on optionalityOrder)
|
||||
// then the first 0 inclusive to switchIndex exclusive members all
|
||||
// have the correct optionality
|
||||
if (isMemberOptional(members[0]) !==
|
||||
(optionalityOrder === 'optional-first')) {
|
||||
report(members[0]);
|
||||
return false;
|
||||
}
|
||||
for (let i = switchIndex + 1; i < members.length; i++) {
|
||||
if (isMemberOptional(members[i]) !==
|
||||
isMemberOptional(members[switchIndex])) {
|
||||
report(members[switchIndex]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Validates if all members are correctly sorted.
|
||||
*
|
||||
* @param members Members to be validated.
|
||||
* @param orderConfig Order config to be validated.
|
||||
* @param supportsModifiers A flag indicating whether the type supports modifiers (scope or accessibility) or not.
|
||||
*/
|
||||
function validateMembersOrder(members, orderConfig, supportsModifiers) {
|
||||
if (orderConfig === 'never') {
|
||||
return;
|
||||
}
|
||||
// Standardize config
|
||||
let order;
|
||||
let memberTypes;
|
||||
let optionalityOrder;
|
||||
/**
|
||||
* It runs an alphabetic sort on the groups of the members of the class in the source code.
|
||||
* @param memberSet The members in the class of the source code on which the grouping operation will be performed.
|
||||
*/
|
||||
const checkAlphaSortForAllMembers = (memberSet) => {
|
||||
const hasAlphaSort = !!(order && order !== 'as-written');
|
||||
if (hasAlphaSort && Array.isArray(memberTypes)) {
|
||||
groupMembersByType(memberSet, memberTypes, supportsModifiers).forEach(members => {
|
||||
checkAlphaSort(members, order);
|
||||
});
|
||||
}
|
||||
};
|
||||
// returns true if everything is good and false if an error was reported
|
||||
const checkOrder = (memberSet) => {
|
||||
const hasAlphaSort = !!(order && order !== 'as-written');
|
||||
// Check order
|
||||
if (Array.isArray(memberTypes)) {
|
||||
const grouped = checkGroupSort(memberSet, memberTypes, supportsModifiers);
|
||||
if (grouped == null) {
|
||||
checkAlphaSortForAllMembers(members);
|
||||
return false;
|
||||
}
|
||||
if (hasAlphaSort) {
|
||||
grouped.map(groupMember => checkAlphaSort(groupMember, order));
|
||||
}
|
||||
}
|
||||
else if (hasAlphaSort) {
|
||||
return checkAlphaSort(memberSet, order);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (Array.isArray(orderConfig)) {
|
||||
memberTypes = orderConfig;
|
||||
}
|
||||
else {
|
||||
order = orderConfig.order;
|
||||
memberTypes = orderConfig.memberTypes;
|
||||
optionalityOrder = orderConfig.optionalityOrder;
|
||||
}
|
||||
if (!optionalityOrder) {
|
||||
checkOrder(members);
|
||||
return;
|
||||
}
|
||||
const switchIndex = members.findIndex((member, i) => i && isMemberOptional(member) !== isMemberOptional(members[i - 1]));
|
||||
if (switchIndex !== -1) {
|
||||
if (!checkRequiredOrder(members, optionalityOrder)) {
|
||||
return;
|
||||
}
|
||||
checkOrder(members.slice(0, switchIndex));
|
||||
checkOrder(members.slice(switchIndex));
|
||||
}
|
||||
else {
|
||||
checkOrder(members);
|
||||
}
|
||||
}
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/5439
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
return {
|
||||
ClassDeclaration(node) {
|
||||
validateMembersOrder(node.body.body, options.classes ?? options.default, true);
|
||||
},
|
||||
'ClassDeclaration, FunctionDeclaration'(node) {
|
||||
if ('superClass' in node) {
|
||||
// ...
|
||||
}
|
||||
},
|
||||
ClassExpression(node) {
|
||||
validateMembersOrder(node.body.body, options.classExpressions ?? options.default, true);
|
||||
},
|
||||
TSInterfaceDeclaration(node) {
|
||||
validateMembersOrder(node.body.body, options.interfaces ?? options.default, false);
|
||||
},
|
||||
TSTypeLiteral(node) {
|
||||
validateMembersOrder(node.members, options.typeLiterals ?? options.default, false);
|
||||
},
|
||||
};
|
||||
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
/// <reference lib="es2015.iterable" />
|
||||
|
||||
interface Generator<T = unknown, TReturn = any, TNext = unknown> extends Iterator<T, TReturn, TNext> {
|
||||
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
|
||||
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
|
||||
return(value: TReturn): IteratorResult<T, TReturn>;
|
||||
throw(e: any): IteratorResult<T, TReturn>;
|
||||
[Symbol.iterator](): Generator<T, TReturn, TNext>;
|
||||
}
|
||||
|
||||
interface GeneratorFunction {
|
||||
/**
|
||||
* Creates a new Generator object.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
new (...args: any[]): Generator;
|
||||
/**
|
||||
* Creates a new Generator object.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
(...args: any[]): Generator;
|
||||
/**
|
||||
* The length of the arguments.
|
||||
*/
|
||||
readonly length: number;
|
||||
/**
|
||||
* Returns the name of the function.
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* A reference to the prototype.
|
||||
*/
|
||||
readonly prototype: Generator;
|
||||
}
|
||||
|
||||
interface GeneratorFunctionConstructor {
|
||||
/**
|
||||
* Creates a new Generator function.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
new (...args: string[]): GeneratorFunction;
|
||||
/**
|
||||
* Creates a new Generator function.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
(...args: string[]): GeneratorFunction;
|
||||
/**
|
||||
* The length of the arguments.
|
||||
*/
|
||||
readonly length: number;
|
||||
/**
|
||||
* Returns the name of the function.
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* A reference to the prototype.
|
||||
*/
|
||||
readonly prototype: GeneratorFunction;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"signatureFlags.js","sourceRoot":"","sources":["../../src/enums/signatureFlags.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,MAAM,CAAC,IAAI,cAAmB,CAAC;AAC/B,CAAC,UAAU,cAAc;IACrB,cAAc,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC;IACpD,cAAc,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,GAAG,kBAAkB,CAAC;IAC5E,cAAc,CAAC,cAAc,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,GAAG,iBAAiB,CAAC;IAC1E,cAAc,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC;IAC9D,cAAc,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC;IAC5D,cAAc,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,GAAG,kBAAkB,CAAC;IAC7E,cAAc,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,GAAG,kBAAkB,CAAC;IAC7E,cAAc,CAAC,cAAc,CAAC,4BAA4B,CAAC,GAAG,EAAE,CAAC,GAAG,4BAA4B,CAAC;IACjG,cAAc,CAAC,cAAc,CAAC,iBAAiB,CAAC,GAAG,GAAG,CAAC,GAAG,iBAAiB,CAAC;IAC5E,cAAc,CAAC,cAAc,CAAC,wCAAwC,CAAC,GAAG,GAAG,CAAC,GAAG,wCAAwC,CAAC;IAC1H,cAAc,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,GAAG,CAAC,GAAG,kBAAkB,CAAC;IAC9E,cAAc,CAAC,cAAc,CAAC,gBAAgB,CAAC,GAAG,EAAE,CAAC,GAAG,gBAAgB,CAAC;AAC7E,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC,CAAC"}
|
||||
@@ -0,0 +1 @@
|
||||
github: mafintosh
|
||||
@@ -0,0 +1 @@
|
||||
(function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"==typeof window?"undefined"==typeof global?"undefined"==typeof self?this:self:global:window,b.base64js=a()}})(function(){return function(){function b(d,e,g){function a(j,i){if(!e[j]){if(!d[j]){var f="function"==typeof require&&require;if(!i&&f)return f(j,!0);if(h)return h(j,!0);var c=new Error("Cannot find module '"+j+"'");throw c.code="MODULE_NOT_FOUND",c}var k=e[j]={exports:{}};d[j][0].call(k.exports,function(b){var c=d[j][1][b];return a(c||b)},k,k.exports,b,d,e,g)}return e[j].exports}for(var h="function"==typeof require&&require,c=0;c<g.length;c++)a(g[c]);return a}return b}()({"/":[function(a,b,c){'use strict';function d(a){var b=a.length;if(0<b%4)throw new Error("Invalid string. Length must be a multiple of 4");var c=a.indexOf("=");-1===c&&(c=b);var d=c===b?0:4-c%4;return[c,d]}function e(a,b,c){return 3*(b+c)/4-c}function f(a){var b,c,f=d(a),g=f[0],h=f[1],j=new m(e(a,g,h)),k=0,n=0<h?g-4:g;for(c=0;c<n;c+=4)b=l[a.charCodeAt(c)]<<18|l[a.charCodeAt(c+1)]<<12|l[a.charCodeAt(c+2)]<<6|l[a.charCodeAt(c+3)],j[k++]=255&b>>16,j[k++]=255&b>>8,j[k++]=255&b;return 2===h&&(b=l[a.charCodeAt(c)]<<2|l[a.charCodeAt(c+1)]>>4,j[k++]=255&b),1===h&&(b=l[a.charCodeAt(c)]<<10|l[a.charCodeAt(c+1)]<<4|l[a.charCodeAt(c+2)]>>2,j[k++]=255&b>>8,j[k++]=255&b),j}function g(a){return k[63&a>>18]+k[63&a>>12]+k[63&a>>6]+k[63&a]}function h(a,b,c){for(var d,e=[],f=b;f<c;f+=3)d=(16711680&a[f]<<16)+(65280&a[f+1]<<8)+(255&a[f+2]),e.push(g(d));return e.join("")}function j(a){for(var b,c=a.length,d=c%3,e=[],f=16383,g=0,j=c-d;g<j;g+=f)e.push(h(a,g,g+f>j?j:g+f));return 1===d?(b=a[c-1],e.push(k[b>>2]+k[63&b<<4]+"==")):2===d&&(b=(a[c-2]<<8)+a[c-1],e.push(k[b>>10]+k[63&b>>4]+k[63&b<<2]+"=")),e.join("")}c.byteLength=function(a){var b=d(a),c=b[0],e=b[1];return 3*(c+e)/4-e},c.toByteArray=f,c.fromByteArray=j;for(var k=[],l=[],m="undefined"==typeof Uint8Array?Array:Uint8Array,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,p=n.length;o<p;++o)k[o]=n[o],l[n.charCodeAt(o)]=o;l[45]=62,l[95]=63},{}]},{},[])("/")});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,551 @@
|
||||
// @ts-ignore TS6133
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import * as z from "zod/v3";
|
||||
import { ZodError, ZodIssueCode } from "../ZodError.js";
|
||||
import { ZodParsedType } from "../helpers/util.js";
|
||||
|
||||
test("error creation", () => {
|
||||
const err1 = ZodError.create([]);
|
||||
err1.addIssue({
|
||||
code: ZodIssueCode.invalid_type,
|
||||
expected: ZodParsedType.object,
|
||||
received: ZodParsedType.string,
|
||||
path: [],
|
||||
message: "",
|
||||
fatal: true,
|
||||
});
|
||||
err1.isEmpty;
|
||||
|
||||
const err2 = ZodError.create(err1.issues);
|
||||
const err3 = new ZodError([]);
|
||||
err3.addIssues(err1.issues);
|
||||
err3.addIssue(err1.issues[0]);
|
||||
err1.message;
|
||||
err2.message;
|
||||
err3.message;
|
||||
});
|
||||
|
||||
const errorMap: z.ZodErrorMap = (error, ctx) => {
|
||||
if (error.code === ZodIssueCode.invalid_type) {
|
||||
if (error.expected === "string") {
|
||||
return { message: "bad type!" };
|
||||
}
|
||||
}
|
||||
if (error.code === ZodIssueCode.custom) {
|
||||
return { message: `less-than-${error.params?.minimum}` };
|
||||
}
|
||||
return { message: ctx.defaultError };
|
||||
};
|
||||
|
||||
test("type error with custom error map", () => {
|
||||
try {
|
||||
z.string().parse(234, { errorMap });
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
|
||||
expect(zerr.issues[0].code).toEqual(z.ZodIssueCode.invalid_type);
|
||||
expect(zerr.issues[0].message).toEqual(`bad type!`);
|
||||
}
|
||||
});
|
||||
|
||||
test("refinement fail with params", () => {
|
||||
try {
|
||||
z.number()
|
||||
.refine((val) => val >= 3, {
|
||||
params: { minimum: 3 },
|
||||
})
|
||||
.parse(2, { errorMap });
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues[0].code).toEqual(z.ZodIssueCode.custom);
|
||||
expect(zerr.issues[0].message).toEqual(`less-than-3`);
|
||||
}
|
||||
});
|
||||
|
||||
test("custom error with custom errormap", () => {
|
||||
try {
|
||||
z.string()
|
||||
.refine((val) => val.length > 12, {
|
||||
params: { minimum: 13 },
|
||||
message: "override",
|
||||
})
|
||||
.parse("asdf", { errorMap });
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues[0].message).toEqual("override");
|
||||
}
|
||||
});
|
||||
|
||||
test("default error message", () => {
|
||||
try {
|
||||
z.number()
|
||||
.refine((x) => x > 3)
|
||||
.parse(2);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues.length).toEqual(1);
|
||||
expect(zerr.issues[0].message).toEqual("Invalid input");
|
||||
}
|
||||
});
|
||||
|
||||
test("override error in refine", () => {
|
||||
try {
|
||||
z.number()
|
||||
.refine((x) => x > 3, "override")
|
||||
.parse(2);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues.length).toEqual(1);
|
||||
expect(zerr.issues[0].message).toEqual("override");
|
||||
}
|
||||
});
|
||||
|
||||
test("override error in refinement", () => {
|
||||
try {
|
||||
z.number()
|
||||
.refine((x) => x > 3, {
|
||||
message: "override",
|
||||
})
|
||||
.parse(2);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues.length).toEqual(1);
|
||||
expect(zerr.issues[0].message).toEqual("override");
|
||||
}
|
||||
});
|
||||
|
||||
test("array minimum", () => {
|
||||
try {
|
||||
z.array(z.string()).min(3, "tooshort").parse(["asdf", "qwer"]);
|
||||
} catch (err) {
|
||||
const zerr: ZodError = err as any;
|
||||
expect(zerr.issues[0].code).toEqual(ZodIssueCode.too_small);
|
||||
expect(zerr.issues[0].message).toEqual("tooshort");
|
||||
}
|
||||
try {
|
||||
z.array(z.string()).min(3).parse(["asdf", "qwer"]);
|
||||
} catch (err) {
|
||||
const zerr: ZodError = err as any;
|
||||
expect(zerr.issues[0].code).toEqual(ZodIssueCode.too_small);
|
||||
expect(zerr.issues[0].message).toEqual(`Array must contain at least 3 element(s)`);
|
||||
}
|
||||
});
|
||||
|
||||
// implement test for semi-smart union logic that checks for type error on either left or right
|
||||
// test("union smart errors", () => {
|
||||
// // expect.assertions(2);
|
||||
|
||||
// const p1 = z
|
||||
// .union([z.string(), z.number().refine((x) => x > 0)])
|
||||
// .safeParse(-3.2);
|
||||
|
||||
// if (p1.success === true) throw new Error();
|
||||
// expect(p1.success).toBe(false);
|
||||
// expect(p1.error.issues[0].code).toEqual(ZodIssueCode.custom);
|
||||
|
||||
// const p2 = z.union([z.string(), z.number()]).safeParse(false);
|
||||
// // .catch(err => expect(err.issues[0].code).toEqual(ZodIssueCode.invalid_union));
|
||||
// if (p2.success === true) throw new Error();
|
||||
// expect(p2.success).toBe(false);
|
||||
// expect(p2.error.issues[0].code).toEqual(ZodIssueCode.invalid_union);
|
||||
// });
|
||||
|
||||
test("custom path in custom error map", () => {
|
||||
const schema = z.object({
|
||||
items: z.array(z.string()).refine((data) => data.length > 3, {
|
||||
path: ["items-too-few"],
|
||||
}),
|
||||
});
|
||||
|
||||
const errorMap: z.ZodErrorMap = (error) => {
|
||||
expect(error.path.length).toBe(2);
|
||||
return { message: "doesnt matter" };
|
||||
};
|
||||
const result = schema.safeParse({ items: ["first"] }, { errorMap });
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0].path).toEqual(["items", "items-too-few"]);
|
||||
}
|
||||
});
|
||||
|
||||
test("error metadata from value", () => {
|
||||
const dynamicRefine = z.string().refine(
|
||||
(val) => val === val.toUpperCase(),
|
||||
(val) => ({ params: { val } })
|
||||
);
|
||||
|
||||
const result = dynamicRefine.safeParse("asdf");
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
const sub = result.error.issues[0];
|
||||
expect(result.error.issues[0].code).toEqual("custom");
|
||||
if (sub.code === "custom") {
|
||||
expect(sub.params!.val).toEqual("asdf");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// test("don't call refine after validation failed", () => {
|
||||
// const asdf = z
|
||||
// .union([
|
||||
// z.number(),
|
||||
// z.string().transform(z.number(), (val) => {
|
||||
// return parseFloat(val);
|
||||
// }),
|
||||
// ])
|
||||
// .refine((v) => v >= 1);
|
||||
|
||||
// expect(() => asdf.safeParse("foo")).not.toThrow();
|
||||
// });
|
||||
|
||||
test("root level formatting", () => {
|
||||
const schema = z.string().email();
|
||||
const result = schema.safeParse("asdfsdf");
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.format()._errors).toEqual(["Invalid email"]);
|
||||
}
|
||||
});
|
||||
|
||||
test("custom path", () => {
|
||||
const schema = z
|
||||
.object({
|
||||
password: z.string(),
|
||||
confirm: z.string(),
|
||||
})
|
||||
.refine((val) => val.confirm === val.password, { path: ["confirm"] });
|
||||
|
||||
const result = schema.safeParse({
|
||||
password: "peanuts",
|
||||
confirm: "qeanuts",
|
||||
});
|
||||
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
// nested errors
|
||||
const error = result.error.format();
|
||||
expect(error._errors).toEqual([]);
|
||||
expect(error.password?._errors).toEqual(undefined);
|
||||
expect(error.confirm?._errors).toEqual(["Invalid input"]);
|
||||
}
|
||||
});
|
||||
|
||||
test("custom path", () => {
|
||||
const schema = z
|
||||
.object({
|
||||
password: z.string().min(6),
|
||||
confirm: z.string().min(6),
|
||||
})
|
||||
.refine((val) => val.confirm === val.password);
|
||||
|
||||
const result = schema.safeParse({
|
||||
password: "qwer",
|
||||
confirm: "asdf",
|
||||
});
|
||||
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.length).toEqual(3);
|
||||
}
|
||||
});
|
||||
|
||||
const schema = z.object({
|
||||
inner: z.object({
|
||||
name: z
|
||||
.string()
|
||||
.refine((val) => val.length > 5)
|
||||
.array()
|
||||
.refine((val) => val.length <= 1),
|
||||
}),
|
||||
});
|
||||
|
||||
test("no abort early on refinements", () => {
|
||||
const invalidItem = {
|
||||
inner: { name: ["aasd", "asdfasdfasfd"] },
|
||||
};
|
||||
|
||||
const result1 = schema.safeParse(invalidItem);
|
||||
expect(result1.success).toEqual(false);
|
||||
if (!result1.success) {
|
||||
expect(result1.error.issues.length).toEqual(2);
|
||||
}
|
||||
});
|
||||
test("formatting", () => {
|
||||
const invalidItem = {
|
||||
inner: { name: ["aasd", "asdfasdfasfd"] },
|
||||
};
|
||||
const invalidArray = {
|
||||
inner: { name: ["asdfasdf", "asdfasdfasfd"] },
|
||||
};
|
||||
const result1 = schema.safeParse(invalidItem);
|
||||
const result2 = schema.safeParse(invalidArray);
|
||||
|
||||
expect(result1.success).toEqual(false);
|
||||
expect(result2.success).toEqual(false);
|
||||
if (!result1.success) {
|
||||
const error = result1.error.format();
|
||||
|
||||
expect(error._errors).toEqual([]);
|
||||
expect(error.inner?._errors).toEqual([]);
|
||||
// expect(error.inner?.name?._errors).toEqual(["Invalid input"]);
|
||||
// expect(error.inner?.name?.[0]._errors).toEqual(["Invalid input"]);
|
||||
expect(error.inner?.name?.[1]).toEqual(undefined);
|
||||
}
|
||||
if (!result2.success) {
|
||||
type FormattedError = z.inferFormattedError<typeof schema>;
|
||||
const error: FormattedError = result2.error.format();
|
||||
expect(error._errors).toEqual([]);
|
||||
expect(error.inner?._errors).toEqual([]);
|
||||
expect(error.inner?.name?._errors).toEqual(["Invalid input"]);
|
||||
expect(error.inner?.name?.[0]).toEqual(undefined);
|
||||
expect(error.inner?.name?.[1]).toEqual(undefined);
|
||||
expect(error.inner?.name?.[2]).toEqual(undefined);
|
||||
}
|
||||
|
||||
// test custom mapper
|
||||
if (!result2.success) {
|
||||
type FormattedError = z.inferFormattedError<typeof schema, number>;
|
||||
const error: FormattedError = result2.error.format(() => 5);
|
||||
expect(error._errors).toEqual([]);
|
||||
expect(error.inner?._errors).toEqual([]);
|
||||
expect(error.inner?.name?._errors).toEqual([5]);
|
||||
}
|
||||
});
|
||||
|
||||
test("formatting with nullable and optional fields", () => {
|
||||
const nameSchema = z.string().refine((val) => val.length > 5);
|
||||
const schema = z.object({
|
||||
nullableObject: z.object({ name: nameSchema }).nullable(),
|
||||
nullableArray: z.array(nameSchema).nullable(),
|
||||
nullableTuple: z.tuple([nameSchema, nameSchema, z.number()]).nullable(),
|
||||
optionalObject: z.object({ name: nameSchema }).optional(),
|
||||
optionalArray: z.array(nameSchema).optional(),
|
||||
optionalTuple: z.tuple([nameSchema, nameSchema, z.number()]).optional(),
|
||||
});
|
||||
const invalidItem = {
|
||||
nullableObject: { name: "abcd" },
|
||||
nullableArray: ["abcd"],
|
||||
nullableTuple: ["abcd", "abcd", 1],
|
||||
optionalObject: { name: "abcd" },
|
||||
optionalArray: ["abcd"],
|
||||
optionalTuple: ["abcd", "abcd", 1],
|
||||
};
|
||||
const result = schema.safeParse(invalidItem);
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
type FormattedError = z.inferFormattedError<typeof schema>;
|
||||
const error: FormattedError = result.error.format();
|
||||
expect(error._errors).toEqual([]);
|
||||
expect(error.nullableObject?._errors).toEqual([]);
|
||||
expect(error.nullableObject?.name?._errors).toEqual(["Invalid input"]);
|
||||
expect(error.nullableArray?._errors).toEqual([]);
|
||||
expect(error.nullableArray?.[0]?._errors).toEqual(["Invalid input"]);
|
||||
expect(error.nullableTuple?._errors).toEqual([]);
|
||||
expect(error.nullableTuple?.[0]?._errors).toEqual(["Invalid input"]);
|
||||
expect(error.nullableTuple?.[1]?._errors).toEqual(["Invalid input"]);
|
||||
expect(error.optionalObject?._errors).toEqual([]);
|
||||
expect(error.optionalObject?.name?._errors).toEqual(["Invalid input"]);
|
||||
expect(error.optionalArray?._errors).toEqual([]);
|
||||
expect(error.optionalArray?.[0]?._errors).toEqual(["Invalid input"]);
|
||||
expect(error.optionalTuple?._errors).toEqual([]);
|
||||
expect(error.optionalTuple?.[0]?._errors).toEqual(["Invalid input"]);
|
||||
expect(error.optionalTuple?.[1]?._errors).toEqual(["Invalid input"]);
|
||||
}
|
||||
});
|
||||
|
||||
const stringWithCustomError = z.string({
|
||||
errorMap: (issue, ctx) => ({
|
||||
message: issue.code === "invalid_type" ? (ctx.data ? "Invalid name" : "Name is required") : ctx.defaultError,
|
||||
}),
|
||||
});
|
||||
|
||||
test("schema-bound error map", () => {
|
||||
const result = stringWithCustomError.safeParse(1234);
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0].message).toEqual("Invalid name");
|
||||
}
|
||||
|
||||
const result2 = stringWithCustomError.safeParse(undefined);
|
||||
expect(result2.success).toEqual(false);
|
||||
if (!result2.success) {
|
||||
expect(result2.error.issues[0].message).toEqual("Name is required");
|
||||
}
|
||||
|
||||
// support contextual override
|
||||
const result3 = stringWithCustomError.safeParse(undefined, {
|
||||
errorMap: () => ({ message: "OVERRIDE" }),
|
||||
});
|
||||
expect(result3.success).toEqual(false);
|
||||
if (!result3.success) {
|
||||
expect(result3.error.issues[0].message).toEqual("OVERRIDE");
|
||||
}
|
||||
});
|
||||
|
||||
test("overrideErrorMap", () => {
|
||||
// support overrideErrorMap
|
||||
z.setErrorMap(() => ({ message: "OVERRIDE" }));
|
||||
const result4 = stringWithCustomError.min(10).safeParse("tooshort");
|
||||
expect(result4.success).toEqual(false);
|
||||
if (!result4.success) {
|
||||
expect(result4.error.issues[0].message).toEqual("OVERRIDE");
|
||||
}
|
||||
z.setErrorMap(z.defaultErrorMap);
|
||||
});
|
||||
|
||||
test("invalid and required", () => {
|
||||
const str = z.string({
|
||||
invalid_type_error: "Invalid name",
|
||||
required_error: "Name is required",
|
||||
});
|
||||
const result1 = str.safeParse(1234);
|
||||
expect(result1.success).toEqual(false);
|
||||
if (!result1.success) {
|
||||
expect(result1.error.issues[0].message).toEqual("Invalid name");
|
||||
}
|
||||
const result2 = str.safeParse(undefined);
|
||||
expect(result2.success).toEqual(false);
|
||||
if (!result2.success) {
|
||||
expect(result2.error.issues[0].message).toEqual("Name is required");
|
||||
}
|
||||
});
|
||||
|
||||
test("Fallback to default required error", () => {
|
||||
const str = z.string({
|
||||
invalid_type_error: "Invalid name",
|
||||
// required_error: "Name is required",
|
||||
});
|
||||
|
||||
const result2 = str.safeParse(undefined);
|
||||
expect(result2.success).toEqual(false);
|
||||
if (!result2.success) {
|
||||
expect(result2.error.issues[0].message).toEqual("Required");
|
||||
}
|
||||
});
|
||||
|
||||
test("invalid and required and errorMap", () => {
|
||||
expect(() => {
|
||||
return z.string({
|
||||
invalid_type_error: "Invalid name",
|
||||
required_error: "Name is required",
|
||||
errorMap: () => ({ message: "OVERRIDE" }),
|
||||
});
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test("strict error message", () => {
|
||||
const errorMsg = "Invalid object";
|
||||
const obj = z.object({ x: z.string() }).strict(errorMsg);
|
||||
const result = obj.safeParse({ x: "a", y: "b" });
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0].message).toEqual(errorMsg);
|
||||
}
|
||||
});
|
||||
|
||||
test("enum error message, invalid enum elementstring", () => {
|
||||
try {
|
||||
z.enum(["Tuna", "Trout"]).parse("Salmon");
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues.length).toEqual(1);
|
||||
expect(zerr.issues[0].message).toEqual("Invalid enum value. Expected 'Tuna' | 'Trout', received 'Salmon'");
|
||||
}
|
||||
});
|
||||
|
||||
test("enum error message, invalid type", () => {
|
||||
try {
|
||||
z.enum(["Tuna", "Trout"]).parse(12);
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues.length).toEqual(1);
|
||||
expect(zerr.issues[0].message).toEqual("Expected 'Tuna' | 'Trout', received number");
|
||||
}
|
||||
});
|
||||
|
||||
test("nativeEnum default error message", () => {
|
||||
enum Fish {
|
||||
Tuna = "Tuna",
|
||||
Trout = "Trout",
|
||||
}
|
||||
try {
|
||||
z.nativeEnum(Fish).parse("Salmon");
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues.length).toEqual(1);
|
||||
expect(zerr.issues[0].message).toEqual("Invalid enum value. Expected 'Tuna' | 'Trout', received 'Salmon'");
|
||||
}
|
||||
});
|
||||
|
||||
test("literal default error message", () => {
|
||||
try {
|
||||
z.literal("Tuna").parse("Trout");
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues.length).toEqual(1);
|
||||
expect(zerr.issues[0].message).toEqual(`Invalid literal value, expected "Tuna"`);
|
||||
}
|
||||
});
|
||||
|
||||
test("literal bigint default error message", () => {
|
||||
try {
|
||||
z.literal(BigInt(12)).parse(BigInt(13));
|
||||
} catch (err) {
|
||||
const zerr: z.ZodError = err as any;
|
||||
expect(zerr.issues.length).toEqual(1);
|
||||
expect(zerr.issues[0].message).toEqual(`Invalid literal value, expected "12"`);
|
||||
}
|
||||
});
|
||||
|
||||
test("enum with message returns the custom error message", () => {
|
||||
const schema = z.enum(["apple", "banana"], {
|
||||
message: "the value provided is invalid",
|
||||
});
|
||||
|
||||
const result1 = schema.safeParse("berries");
|
||||
expect(result1.success).toEqual(false);
|
||||
if (!result1.success) {
|
||||
expect(result1.error.issues[0].message).toEqual("the value provided is invalid");
|
||||
}
|
||||
|
||||
const result2 = schema.safeParse(undefined);
|
||||
expect(result2.success).toEqual(false);
|
||||
if (!result2.success) {
|
||||
expect(result2.error.issues[0].message).toEqual("the value provided is invalid");
|
||||
}
|
||||
|
||||
const result3 = schema.safeParse("banana");
|
||||
expect(result3.success).toEqual(true);
|
||||
|
||||
const result4 = schema.safeParse(null);
|
||||
expect(result4.success).toEqual(false);
|
||||
if (!result4.success) {
|
||||
expect(result4.error.issues[0].message).toEqual("the value provided is invalid");
|
||||
}
|
||||
});
|
||||
|
||||
test("when the message is falsy, it is used as is provided", () => {
|
||||
const schema = z.string().max(1, { message: "" });
|
||||
const result = schema.safeParse("asdf");
|
||||
expect(result.success).toEqual(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0].message).toEqual("");
|
||||
}
|
||||
});
|
||||
|
||||
// test("dont short circuit on continuable errors", () => {
|
||||
// const user = z
|
||||
// .object({
|
||||
// password: z.string().min(6),
|
||||
// confirm: z.string(),
|
||||
// })
|
||||
// .refine((data) => data.password === data.confirm, {
|
||||
// message: "Passwords don't match",
|
||||
// path: ["confirm"],
|
||||
// });
|
||||
// const result = user.safeParse({ password: "asdf", confirm: "qwer" });
|
||||
// if (!result.success) {
|
||||
// expect(result.error.issues.length).toEqual(2);
|
||||
// }
|
||||
// });
|
||||
@@ -0,0 +1,6 @@
|
||||
import { _ as _array_like_to_array } from "./_array_like_to_array.js";
|
||||
|
||||
function _array_without_holes(arr) {
|
||||
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
||||
}
|
||||
export { _array_without_holes as _ };
|
||||
@@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.LegacyESLint = void 0;
|
||||
const eslint_1 = require("eslint");
|
||||
const use_at_your_own_risk_1 = __importDefault(require("eslint/use-at-your-own-risk"));
|
||||
function throwMissingLegacyESLintError() {
|
||||
throw new Error('LegacyESLint is not available with the current version of ESLint.');
|
||||
}
|
||||
/* eslint-disable-next-line @typescript-eslint/no-extraneous-class */
|
||||
class MissingLegacyESLint {
|
||||
static configType = 'eslintrc';
|
||||
static version = eslint_1.ESLint.version;
|
||||
constructor() {
|
||||
throwMissingLegacyESLintError();
|
||||
}
|
||||
static getErrorResults() {
|
||||
throwMissingLegacyESLintError();
|
||||
}
|
||||
static outputFixes() {
|
||||
throwMissingLegacyESLintError();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The ESLint class is the primary class to use in Node.js applications.
|
||||
* This class depends on the Node.js fs module and the file system, so you cannot use it in browsers.
|
||||
*
|
||||
* If you want to lint code on browsers, use the Linter class instead.
|
||||
*/
|
||||
class LegacyESLint extends (use_at_your_own_risk_1.default.LegacyESLint ??
|
||||
MissingLegacyESLint) {
|
||||
}
|
||||
exports.LegacyESLint = LegacyESLint;
|
||||
Reference in New Issue
Block a user