diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index dec180a..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,177 +0,0 @@ -{ - "root": true, - "env": { - "es2020": true, - "node": true, - "browser": true - }, - "parserOptions": { - "ecmaVersion": 2023, - "sourceType": "module" - }, - "extends": [ - "eslint:recommended", - "plugin:import/recommended" - ], - "plugins": ["import"], - "settings": { - "import/resolver": { - "node": {}, - "exports": {} - } - }, - "rules": { - "array-bracket-newline": ["error", "consistent"], - "array-bracket-spacing": "error", - "arrow-parens": "error", - "arrow-spacing": "error", - "block-spacing": "error", - "brace-style": "error", - "comma-dangle": "error", - "comma-spacing": "error", - "comma-style": "error", - "curly": "error", - "default-param-last": "error", - "dot-location": ["error", "property"], - "dot-notation": "error", - "eol-last": "error", - "eqeqeq": "error", - "func-call-spacing": "error", - "function-paren-newline": "error", - "generator-star-spacing": ["error", { "before": false, "after": true }], - "implicit-arrow-linebreak": "error", - "indent": ["error", 2, { "ignoreComments": true, "SwitchCase": 1 }], - "key-spacing": "error", - "keyword-spacing": "error", - "linebreak-style": "error", - "max-len": ["warn", { "code": 160, "ignoreStrings": true, "ignoreTemplateLiterals": true, "ignoreRegExpLiterals": true }], - "new-parens": "error", - "no-caller": "error", - "no-case-declarations": "off", - "no-console": "error", - "no-constant-binary-expression": "error", - "no-constructor-return": "error", - "no-duplicate-imports": "error", - "no-empty-static-block": "error", - "no-eval": "error", - "no-extend-native": "error", - "no-extra-bind": "error", - "no-extra-label": "error", - "no-extra-parens": ["error", "all", { "nestedBinaryExpressions": false }], - "no-fallthrough": "off", - "no-floating-decimal": "error", - "no-implicit-globals": "error", - "no-invalid-this": "error", - "no-iterator": "error", - "no-loop-func": "error", - "no-multi-spaces": "error", - "no-multiple-empty-lines": "error", - "no-new-native-nonconstructor": "error", - "no-object-constructor": "error", - "no-promise-executor-return": "error", - "no-proto": "error", - "no-return-assign": "error", - "no-self-compare": "error", - "no-sequences": "error", - "no-tabs": "error", - "no-template-curly-in-string": "error", - "no-throw-literal": "error", - "no-trailing-spaces": "error", - "no-undef-init": "error", - "no-unmodified-loop-condition": "error", - "no-unused-private-class-members": "error", - "no-unused-vars": ["error", { "argsIgnorePattern": "_.*" }], - "no-useless-call": "error", - "no-useless-computed-key": "error", - "no-useless-concat": "error", - "no-useless-rename": "error", - "no-whitespace-before-property": "error", - "object-curly-newline": "error", - "object-curly-spacing": ["error", "always"], - "object-property-newline": ["error", { "allowAllPropertiesOnSameLine": true }], - "operator-linebreak": ["error", "before"], - "padded-blocks": ["error", "never"], - "prefer-arrow-callback": "error", - "prefer-const": ["error", { "destructuring": "all" }], - "prefer-numeric-literals": "error", - "prefer-object-has-own": "error", - "prefer-object-spread": "error", - "prefer-promise-reject-errors": "error", - "prefer-rest-params": "error", - "prefer-spread": "error", - "quotes": ["error", "double", { "allowTemplateLiterals": true }], - "radix": "error", - "rest-spread-spacing": "error", - "semi": "error", - "semi-spacing": "error", - "semi-style": "error", - "space-before-blocks": "error", - "space-before-function-paren": ["error", { "anonymous": "always", "named": "never", "asyncArrow": "always" }], - "space-in-parens": "error", - "space-infix-ops": "error", - "space-unary-ops": "error", - "strict": "error", - "switch-colon-spacing": "error", - "template-curly-spacing": "error", - "template-tag-spacing": "error", - "unicode-bom": "error", - "wrap-iife": "error", - "yield-star-spacing": "error", - "yoda": "error", - "import/extensions": ["error", "always", { "js": "always", "ignorePackages": true }], - "import/newline-after-import": ["error", { "count": 2 }] - }, - "overrides": [ - { - "files": ["**/*.ts"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking" - ], - "parserOptions": { - "project": "./tsconfig.json" - }, - "settings": { - "import/resolver": "typescript" - }, - "rules": { - "no-prototype-builtins": ["off"], - "import/extensions": ["error", "always", { "ts": "never" }], - "@typescript-eslint/array-type": "error", - "@typescript-eslint/consistent-type-assertions": "error", - "@typescript-eslint/consistent-type-definitions": ["error", "type"], - "@typescript-eslint/consistent-type-imports": "error", - "default-param-last": "off", - "@typescript-eslint/default-param-last": "error", - "@typescript-eslint/method-signature-style": "error", - "no-duplicate-imports": "off", - "@typescript-eslint/no-base-to-string": "error", - "@typescript-eslint/no-confusing-void-expression": "error", - "no-loss-of-precision": "off", - "@typescript-eslint/no-loss-of-precision": "error", - "@typescript-eslint/no-meaningless-void-operator": "error", - "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", - "@typescript-eslint/no-unnecessary-condition": "error", - "@typescript-eslint/no-unnecessary-qualifier": "error", - "@typescript-eslint/no-unnecessary-type-arguments": "error", - "@typescript-eslint/no-unnecessary-type-constraint": "error", - "@typescript-eslint/no-unsafe-argument": "warn", - "@typescript-eslint/no-unsafe-assignment": "warn", - "@typescript-eslint/no-unsafe-call": "warn", - "@typescript-eslint/no-unsafe-member-access": "warn", - "@typescript-eslint/no-unsafe-return": "warn", - "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "_.*" }], - "no-unused-expressions": "off", - "@typescript-eslint/no-unused-expressions": "error", - "@typescript-eslint/non-nullable-type-assertion-style": "error", - "@typescript-eslint/prefer-for-of": "error", - "@typescript-eslint/prefer-reduce-type-parameter": "error", - "no-return-await": "off", - "@typescript-eslint/require-await": ["off"], - "@typescript-eslint/return-await": "error" - } - } - ] -} diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 34da3ab..48ebcfc 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -23,3 +23,4 @@ jobs: - run: npm install - run: npm test - run: npm run lint + - run: npm run type-check diff --git a/.npmignore b/.npmignore index c036575..d067d82 100644 --- a/.npmignore +++ b/.npmignore @@ -1,10 +1,8 @@ .github/ -.eslintrc.json +eslint.config.js **/*.spec.ts tsconfig.json lib/jref/SPECIFICATION.md scratch/ TODO -rollup.config.js -dist/ lib/urn-scheme-plugin.js diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..d5f3f34 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,66 @@ +import js from "@eslint/js"; +import stylistic from "@stylistic/eslint-plugin"; +import importPlugin from "eslint-plugin-import"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + + +export default [ + js.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + importPlugin.flatConfigs.recommended, + stylistic.configs.customize({ + arrowParens: true, + braceStyle: "1tbs", + commaDangle: "never", + flat: true, + jsx: false, + quotes: "double", + semi: true + }), + { + languageOptions: { + ecmaVersion: "latest", + globals: { + ...globals.node + }, + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname + } + }, + settings: { + "import/resolver": { + node: {}, + typescript: {} + } + }, + rules: { + // JavaScript + "no-console": ["error"], + "no-empty-function": "off", + "no-fallthrough": "off", + + // TypeScript + "@typescript-eslint/no-unused-vars": ["error", { caughtErrorsIgnorePattern: "^_" }], + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/consistent-type-definitions": ["error", "type"], + + // Imports + "import/extensions": ["error", "ignorePackages"], + "import/newline-after-import": ["error", { count: 2, exactCount: false, considerComments: true }], + + // Stylistic + "@stylistic/multiline-ternary": "off", + "@stylistic/no-mixed-operators": "off", + "@stylistic/no-multiple-empty-lines": ["error", { max: 2, maxEOF: 0, maxBOF: 0 }], // Allow max=2 for imports + "@stylistic/quote-props": ["error", "consistent"], + "@stylistic/yield-star-spacing": ["error", "after"] + } + }, + { + files: ["**/*.js"], + ...tseslint.configs.disableTypeChecked + } +]; diff --git a/lib/browser/embedded.spec.ts b/lib/browser/embedded.spec.ts index 061bafe..37c90d0 100644 --- a/lib/browser/embedded.spec.ts +++ b/lib/browser/embedded.spec.ts @@ -1,4 +1,4 @@ -import { describe, it, beforeEach, afterEach, expect, beforeAll, afterAll } from "vitest"; +import { describe, test, beforeEach, afterEach, expect, beforeAll, afterAll } from "vitest"; import { MockAgent, setGlobalDispatcher } from "undici"; import { get, addMediaTypePlugin, removeMediaTypePlugin } from "../index.js"; import { parse, stringify } from "../jref/index.js"; @@ -32,7 +32,7 @@ describe("JSON Browser", () => { }; addMediaTypePlugin(testMediaType, { parse: async (response) => parseToDocument(response.url, await response.text()), - fileMatcher: async (path) => path.endsWith(".embedded") + fileMatcher: async (path) => path.endsWith(".embedded") // eslint-disable-line @typescript-eslint/require-await }); }); @@ -42,7 +42,7 @@ describe("JSON Browser", () => { let mockAgent: MockAgent; - beforeEach(async () => { + beforeEach(() => { mockAgent = new MockAgent(); mockAgent.disableNetConnect(); setGlobalDispatcher(mockAgent); @@ -52,7 +52,7 @@ describe("JSON Browser", () => { await mockAgent.close(); }); - it("getting an embedded document", async () => { + test("getting an embedded document", async () => { const jrefEmbedded = `{ "$embedded": { "${testDomain}/foo": {} @@ -67,7 +67,7 @@ describe("JSON Browser", () => { expect(subject.uri).to.equal(`${testDomain}/foo`); }); - it("getting the main document from an embedded document", async () => { + test("getting the main document from an embedded document", async () => { const jrefEmbedded = `{ "$embedded": { "${testDomain}/foo": {} @@ -83,7 +83,7 @@ describe("JSON Browser", () => { expect(subject.uri).to.equal(`${testDomain}/main`); }); - it("getting an embedded document from an embedded document", async () => { + test("getting an embedded document from an embedded document", async () => { const jrefEmbedded = `{ "$embedded": { "${testDomain}/foo": {}, @@ -100,7 +100,7 @@ describe("JSON Browser", () => { expect(subject.uri).to.equal(`${testDomain}/bar`); }); - it("referencing an embedded document", async () => { + test("referencing an embedded document", async () => { const jrefEmbedded = `{ "foo": { "$ref": "/foo" }, @@ -116,7 +116,7 @@ describe("JSON Browser", () => { expect(subject.uri).to.equal(`${testDomain}/foo`); }); - it("referencing the main document from an embedded document", async () => { + test("referencing the main document from an embedded document", async () => { const jrefEmbedded = `{ "$embedded": { "${testDomain}/foo": { @@ -133,7 +133,7 @@ describe("JSON Browser", () => { expect(subject.uri).to.equal(`${testDomain}/main`); }); - it("referencing an embedded document from an embedded document", async () => { + test("referencing an embedded document from an embedded document", async () => { const jrefEmbedded = `{ "$embedded": { "${testDomain}/foo": { @@ -151,7 +151,7 @@ describe("JSON Browser", () => { expect(subject.uri).to.equal(`${testDomain}/bar`); }); - it("a cached document takes precence over an embedded document", async () => { + test("a cached document takes precence over an embedded document", async () => { const cachedJrefEmbedded = `{ "foo": { "$ref": "/main" } }`; diff --git a/lib/browser/generators.spec.ts b/lib/browser/generators.spec.ts index aa4f8eb..bb7b7e9 100644 --- a/lib/browser/generators.spec.ts +++ b/lib/browser/generators.spec.ts @@ -1,4 +1,4 @@ -import { describe, it, beforeEach, afterEach, expect } from "vitest"; +import { describe, test, beforeEach, afterEach, expect } from "vitest"; import { MockAgent, setGlobalDispatcher } from "undici"; import { get, value, iter, keys, values, entries } from "../index.js"; import type { Browser } from "../index.js"; @@ -19,7 +19,7 @@ describe("JSON Browser", () => { await mockAgent.close(); }); - it("iter", async () => { + test("iter", async () => { const jref = `[1, { "$ref": "/external" }, { "$ref": "#/1" }]`; const external = "2"; @@ -45,7 +45,7 @@ describe("JSON Browser", () => { expect((await generator.next()).done).to.equal(true); }); - it("keys", async () => { + test("keys", async () => { const jref = `{ "a": 1, "b": { "$ref": "/external" }, @@ -65,7 +65,7 @@ describe("JSON Browser", () => { expect(generator.next().done).to.equal(true); }); - it("values", async () => { + test("values", async () => { const jref = `{ "a": 1, "b": { "$ref": "/external" }, @@ -95,7 +95,7 @@ describe("JSON Browser", () => { expect((await generator.next()).done).to.equal(true); }); - it("entries", async () => { + test("entries", async () => { const jref = `{ "a": 1, "b": { "$ref": "/external" }, diff --git a/lib/browser/get.spec.ts b/lib/browser/get.spec.ts index 52eb0f2..2b4655c 100644 --- a/lib/browser/get.spec.ts +++ b/lib/browser/get.spec.ts @@ -1,4 +1,4 @@ -import { describe, it, beforeEach, afterEach, expect } from "vitest"; +import { describe, test, beforeEach, afterEach, expect } from "vitest"; import { MockAgent, setGlobalDispatcher } from "undici"; import { get } from "../index.js"; import { Reference } from "../jref/index.js"; @@ -19,7 +19,7 @@ describe("JSON Browser", () => { await mockAgent.close(); }); - it("follow references", async () => { + test("follow references", async () => { const path = "/foo"; const fragment = "/bar"; const href = "#/foo"; @@ -44,7 +44,7 @@ describe("JSON Browser", () => { expect(browser.document.root).to.eql({ foo: 42, bar: new Reference(href) }); }); - it("relative to browser", async () => { + test("relative to browser", async () => { const path = "/foo"; const href = "/bar"; const foo = `{ @@ -70,7 +70,7 @@ describe("JSON Browser", () => { expect(browser.document.root).to.eql("bar"); }); - it("fragment-only relative to browser", async () => { + test("fragment-only relative to browser", async () => { const path = "/foo"; const fragment = "/foo"; const href = "#/foo"; diff --git a/lib/browser/step.spec.ts b/lib/browser/step.spec.ts index 960dd60..e115ab9 100644 --- a/lib/browser/step.spec.ts +++ b/lib/browser/step.spec.ts @@ -1,4 +1,4 @@ -import { describe, it, beforeEach, afterEach, expect } from "vitest"; +import { describe, test, beforeEach, afterEach, expect } from "vitest"; import { MockAgent, setGlobalDispatcher } from "undici"; import { get, step, value } from "../index.js"; import type { Browser } from "../index.js"; @@ -11,7 +11,7 @@ describe("JSON Browser", () => { const testDomain = "https://example.com"; - beforeEach(async () => { + beforeEach(() => { mockAgent = new MockAgent(); mockAgent.disableNetConnect(); setGlobalDispatcher(mockAgent); @@ -21,7 +21,7 @@ describe("JSON Browser", () => { await mockAgent.close(); }); - it("value", async () => { + test("value", async () => { const path = "/subject"; const jref = `{ "foo": 42 }`; @@ -38,7 +38,7 @@ describe("JSON Browser", () => { expect(value(foo)).to.equal(42); }); - it("local reference to value", async () => { + test("local reference to value", async () => { const path = "/subject"; const jref = `{ "foo": 42, @@ -58,7 +58,7 @@ describe("JSON Browser", () => { expect(value(foo)).to.equal(42); }); - it("local reference to local reference to value", async () => { + test("local reference to local reference to value", async () => { const path = "/subject"; const jref = `{ "foo": 42, @@ -79,7 +79,7 @@ describe("JSON Browser", () => { expect(value(foo)).to.equal(42); }); - it("local reference to external reference to value", async () => { + test("local reference to external reference to value", async () => { const subjectPath = "/subject"; const subjectJref = `{ "bar": { "$ref": "/external#/foo" }, @@ -106,7 +106,7 @@ describe("JSON Browser", () => { expect(value(foo)).to.equal(42); }); - it("external reference to value", async () => { + test("external reference to value", async () => { const subjectPath = "/subject"; const subjectJref = `{ "bar": { "$ref": "/external#/foo" } @@ -133,7 +133,7 @@ describe("JSON Browser", () => { expect(value(foo)).to.equal(42); }); - it("external reference to local reference to value", async () => { + test("external reference to local reference to value", async () => { const subjectPath = "/subject"; const subjectJref = `{ "baz": { "$ref": "/external#/bar" } diff --git a/lib/browser/value.spec.ts b/lib/browser/value.spec.ts index 5581b33..15a33f2 100644 --- a/lib/browser/value.spec.ts +++ b/lib/browser/value.spec.ts @@ -1,4 +1,4 @@ -import { describe, it, beforeEach, afterEach, expect } from "vitest"; +import { describe, test, beforeEach, afterEach, expect } from "vitest"; import { MockAgent, setGlobalDispatcher } from "undici"; import { get, value } from "../index.js"; import { Reference } from "../jref/index.js"; @@ -19,7 +19,7 @@ describe("JSON Browser", () => { await mockAgent.close(); }); - it("full document", async () => { + test("full document", async () => { const path = "/foo"; const href = "/bar"; const jref = `{ @@ -37,7 +37,7 @@ describe("JSON Browser", () => { expect(value(browser)).to.eql({ foo: 42, bar: new Reference(href) }); }); - it("with pointer", async () => { + test("with pointer", async () => { const path = "/foo"; const fragment = "/foo"; const href = "#/foo"; @@ -56,7 +56,7 @@ describe("JSON Browser", () => { expect(value(browser)).to.equal(42); }); - it("with pointer to reference", async () => { + test("with pointer to reference", async () => { const path = "/foo"; const fragment = "/bar"; const href = "#/foo"; diff --git a/lib/jref/index.d.ts b/lib/jref/index.d.ts index 89c7386..a9c59c4 100644 --- a/lib/jref/index.d.ts +++ b/lib/jref/index.d.ts @@ -1,5 +1,5 @@ export type JRef = null | boolean | string | number | Reference | JRefObject | JRef[]; -export type JRefObject = { +export type JRefObject = { // eslint-disable-line @typescript-eslint/consistent-indexed-object-style [property: string]: JRef; }; diff --git a/lib/jref/index.js b/lib/jref/index.js index a143559..102ff24 100644 --- a/lib/jref/index.js +++ b/lib/jref/index.js @@ -47,8 +47,9 @@ export const jrefTypeOf = (value) => { } else if (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null) { return "object"; } - default: + default: { const type = jsType === "object" ? Object.getPrototypeOf(value).constructor.name || "anonymous" : jsType; throw Error(`Not a JRef compatible type: ${type}`); + } } }; diff --git a/lib/jref/index.spec.ts b/lib/jref/index.spec.ts index 70824f1..abf5b19 100644 --- a/lib/jref/index.spec.ts +++ b/lib/jref/index.spec.ts @@ -1,4 +1,4 @@ -import { describe, it, beforeAll, expect } from "vitest"; +import { describe, test, beforeAll, expect } from "vitest"; import { parse, stringify, Reference } from "./index.js"; @@ -7,85 +7,85 @@ const uri = "./foo"; describe("JRef", () => { describe("parse", () => { describe("scalars", () => { - it("null", () => { + test("null", () => { const subject = parse(`null`); expect(subject).to.equal(null); }); - it("true", () => { + test("true", () => { const subject = parse(`true`); expect(subject).to.equal(true); }); - it("false", () => { + test("false", () => { const subject = parse(`false`); expect(subject).to.equal(false); }); - it("integers", () => { + test("integers", () => { const subject = parse(`1`); expect(subject).to.equal(1); }); - it("real numbers", () => { + test("real numbers", () => { const subject = parse(`1.34`); expect(subject).to.equal(1.34); }); - it("negative numbers", () => { + test("negative numbers", () => { const subject = parse(`-1.34`); expect(subject).to.equal(-1.34); }); - it("exponential numbers", () => { + test("exponential numbers", () => { const subject = parse(`-1e34`); expect(subject).to.equal(-1e34); }); - it("reference", () => { + test("reference", () => { const subject = parse(`{ "$ref": "${uri}" }`); expect(subject).to.eql(new Reference(uri)); }); }); describe("array", () => { - it("empty", () => { + test("empty", () => { const subject = parse(`[]`); expect(subject).to.eql([]); }); - it("non-empty", () => { + test("non-empty", () => { const subject = parse(`["foo", 42]`); expect(subject).to.eql(["foo", 42]); }); - it("reference", () => { + test("reference", () => { const subject = parse(`["foo", { "$ref": "${uri}" }, 42]`); expect(subject).to.be.eql(["foo", new Reference(uri), 42]); }); }); describe("object", () => { - it("empty", () => { + test("empty", () => { const subject = parse(`{}`); expect(subject).to.eql({}); }); - it("non-empty", () => { + test("non-empty", () => { const subject = parse(`{ "foo": 42 }`); expect(subject).to.eql({ foo: 42 }); }); - it("reference", () => { + test("reference", () => { const subject = parse(`{ "foo": 42, "bar": { "$ref": "${uri}" } }`); expect(subject).to.be.eql({ foo: 42, bar: new Reference(uri) }); }); }); describe("reviver", () => { - it("convert properties that start with 'i' to integers", () => { + test("convert properties that start with 'i' to integers", () => { const subject = parse(`{ "foo": 42, "iBar": "42", "baz": { "$ref": "${uri}" } }`, (key, value) => { - return key[0] === "i" && typeof value === "string" ? parseInt(value, 10) : value; + return key.startsWith("i") && typeof value === "string" ? parseInt(value, 10) : value; }); expect(subject).to.eql({ foo: 42, iBar: 42, baz: new Reference(uri) }); }); @@ -94,92 +94,92 @@ describe("JRef", () => { describe("stringify", () => { describe("scalars", () => { - it("null", () => { + test("null", () => { const subject = stringify(null); expect(subject).to.equal(`null`); }); - it("true", () => { + test("true", () => { const subject = stringify(true); expect(subject).to.equal(`true`); }); - it("false", () => { + test("false", () => { const subject = stringify(false); expect(subject).to.equal(`false`); }); - it("integers", () => { + test("integers", () => { const subject = stringify(1); expect(subject).to.equal(`1`); }); - it("real numbers", () => { + test("real numbers", () => { const subject = stringify(1.34); expect(subject).to.equal(`1.34`); }); - it("negative numbers", () => { + test("negative numbers", () => { const subject = stringify(-1.34); expect(subject).to.equal(`-1.34`); }); - it("exponential numbers", () => { + test("exponential numbers", () => { const subject = stringify(-1e34); expect(subject).to.equal(`-1e+34`); }); - it("reference", () => { + test("reference", () => { const subject = stringify(new Reference(uri)); expect(subject).to.equal(`{"$ref":"${uri}"}`); }); }); describe("array", () => { - it("empty", () => { + test("empty", () => { const subject = stringify([]); expect(subject).to.eql(`[]`); }); - it("non-empty", () => { + test("non-empty", () => { const subject = stringify(["foo", 42]); expect(subject).to.eql(`["foo",42]`); }); - it("reference", () => { + test("reference", () => { const subject = stringify(["foo", new Reference(uri), 42]); expect(subject).to.be.equal(`["foo",{"$ref":"${uri}"},42]`); }); }); describe("object", () => { - it("empty", () => { + test("empty", () => { const subject = stringify({}); expect(subject).to.eql(`{}`); }); - it("non-empty", () => { + test("non-empty", () => { const subject = stringify({ "foo": 42 }); expect(subject).to.eql(`{"foo":42}`); }); - it("reference", () => { + test("reference", () => { const subject = stringify({ foo: 42, bar: new Reference(uri) }); expect(subject).to.equal(`{"foo":42,"bar":{"$ref":"${uri}"}}`); }); }); describe("replacer", () => { - it("convert properties that start with 'i' to strings", () => { + test("convert properties that start with 'i' to strings", () => { const subject = stringify({ foo: 42, iBar: 42, baz: new Reference(uri) }, (key, value) => { - return key[0] === "i" && typeof value === "number" ? String(value) : value; + return key.startsWith("i") && typeof value === "number" ? String(value) : value; }); expect(subject).to.equal(`{"foo":42,"iBar":"42","baz":{"$ref":"${uri}"}}`); }); }); describe("space", () => { - it("pretty print", () => { + test("pretty print", () => { const subject = stringify({ foo: 42, bar: new Reference(uri) }, null, " "); expect(subject).to.equal(`{ "foo": 42, @@ -200,11 +200,11 @@ describe("JRef", () => { subject = new Reference(uri); }); - it("$ref getter", () => { + test("$ref getter", () => { expect(subject.href).to.equal(uri); }); - it("to JSON", () => { + test("to JSON", () => { expect(JSON.stringify(subject)).to.equal(`{"$ref":"${uri}"}`); }); }); @@ -217,11 +217,11 @@ describe("JRef", () => { subject = new Reference(uri, [uri]); }); - it("$ref getter", () => { + test("$ref getter", () => { expect(subject.href).to.equal(uri); }); - it("to JSON", () => { + test("to JSON", () => { expect(JSON.stringify(subject)).to.eql(`["${uri}"]`); }); }); diff --git a/lib/jref/jref-typeof.spec.ts b/lib/jref/jref-typeof.spec.ts index 099497b..82fdd41 100644 --- a/lib/jref/jref-typeof.spec.ts +++ b/lib/jref/jref-typeof.spec.ts @@ -1,68 +1,68 @@ -import { describe, it, expect } from "vitest"; +import { describe, test, expect } from "vitest"; import { Reference, jrefTypeOf } from "../jref/index.js"; describe("JRef - jrefTypeOf", () => { - it("null", () => { + test("null", () => { expect(jrefTypeOf(null)).to.equal("null"); }); - it("boolean - true", () => { + test("boolean - true", () => { expect(jrefTypeOf(true)).to.equal("boolean"); }); - it("boolean - false", () => { + test("boolean - false", () => { expect(jrefTypeOf(true)).to.equal("boolean"); }); - it("number", () => { + test("number", () => { expect(jrefTypeOf(42)).to.equal("number"); }); - it("bigint", () => { + test("bigint", () => { expect(jrefTypeOf(42n)).to.equal("number"); }); - it("string", () => { + test("string", () => { expect(jrefTypeOf("")).to.equal("string"); }); - it("array", () => { + test("array", () => { expect(jrefTypeOf([])).to.equal("array"); }); - it("object", () => { + test("object", () => { expect(jrefTypeOf({})).to.equal("object"); }); - it("null prototype object", () => { + test("null prototype object", () => { expect(jrefTypeOf(Object.create(null))).to.equal("object"); }); - it("reference", () => { + test("reference", () => { expect(jrefTypeOf(new Reference("/"))).to.equal("reference"); }); - it("undefined", () => { + test("undefined", () => { expect(jrefTypeOf(undefined)).to.equal("undefined"); }); - it("symbol", () => { + test("symbol", () => { const subject = Symbol("test"); expect(() => jrefTypeOf(subject)).to.throw(Error, "Not a JRef compatible type: symbol"); }); - it("function", () => { + test("function", () => { const subject = () => true; expect(() => jrefTypeOf(subject)).to.throw(Error, "Not a JRef compatible type: function"); }); - it("non-plain-object", () => { + test("non-plain-object", () => { const subject = new Set(); expect(() => jrefTypeOf(subject)).to.throw(Error, "Not a JRef compatible type: Set"); }); - it("anonymous non-plain-object", () => { + test("anonymous non-plain-object", () => { const subject = new class {}(); expect(() => jrefTypeOf(subject)).to.throw(Error, "Not a JRef compatible type: anonymous"); }); diff --git a/lib/media-types/media-types.spec.ts b/lib/media-types/media-types.spec.ts index 27d65cf..186c69f 100644 --- a/lib/media-types/media-types.spec.ts +++ b/lib/media-types/media-types.spec.ts @@ -1,11 +1,11 @@ -import { describe, it, beforeEach, afterEach, expect } from "vitest"; +import { describe, test, beforeEach, afterEach, expect } from "vitest"; import { addMediaTypePlugin, removeMediaTypePlugin, setMediaTypeQuality } from "../index.js"; import { acceptableMediaTypes } from "./media-types.js"; describe("JSON Browser", () => { describe("media types", () => { - it("default", () => { + test("default", () => { const accept = acceptableMediaTypes() as string; expect(accept).to.equal("application/reference+json, */*; q=0.001"); }); @@ -13,14 +13,14 @@ describe("JSON Browser", () => { describe("media type plugin without quality", () => { beforeEach(() => { addMediaTypePlugin("application/foo", { - parse: async () => { + parse: async () => { // eslint-disable-line @typescript-eslint/require-await return { baseUri: "https://example.com/foo", root: null, anchorLocation: (fragment) => fragment ?? "" }; }, - fileMatcher: async (path) => path.endsWith(".foo") + fileMatcher: async (path) => path.endsWith(".foo") // eslint-disable-line @typescript-eslint/require-await }); }); @@ -28,18 +28,18 @@ describe("JSON Browser", () => { removeMediaTypePlugin("application/foo"); }); - it("addMediaTypePlugin", () => { + test("addMediaTypePlugin", () => { const accept = acceptableMediaTypes() as string; expect(accept).to.equal("application/reference+json, application/foo, */*; q=0.001"); }); - it("removeMediaTypePlugin", () => { + test("removeMediaTypePlugin", () => { removeMediaTypePlugin("application/foo"); const accept = acceptableMediaTypes() as string; expect(accept).to.equal("application/reference+json, */*; q=0.001"); }); - it("setMediaTypeQuality", () => { + test("setMediaTypeQuality", () => { setMediaTypeQuality("application/foo", 0.9); const accept = acceptableMediaTypes() as string; expect(accept).to.equal("application/reference+json, application/foo; q=0.9, */*; q=0.001"); @@ -49,14 +49,14 @@ describe("JSON Browser", () => { describe("media type plugin with quality", () => { beforeEach(() => { addMediaTypePlugin("application/foo", { - parse: async () => { + parse: async () => { // eslint-disable-line @typescript-eslint/require-await return { baseUri: "https://exmple.com/foo", root: null, anchorLocation: (fragment) => fragment ?? "" }; }, - fileMatcher: async (path) => path.endsWith(".foo"), + fileMatcher: async (path) => path.endsWith(".foo"), // eslint-disable-line @typescript-eslint/require-await quality: 0.8 }); }); @@ -65,12 +65,12 @@ describe("JSON Browser", () => { removeMediaTypePlugin("application/foo"); }); - it("addMediaTypePlugin", () => { + test("addMediaTypePlugin", () => { const accept = acceptableMediaTypes() as string; expect(accept).to.equal("application/reference+json, application/foo; q=0.8, */*; q=0.001"); }); - it("setMediaTypeQuality", () => { + test("setMediaTypeQuality", () => { setMediaTypeQuality("application/foo", 0.9); const accept = acceptableMediaTypes() as string; expect(accept).to.equal("application/reference+json, application/foo; q=0.9, */*; q=0.001"); diff --git a/lib/uri-schemes/file-scheme-plugin.spec.ts b/lib/uri-schemes/file-scheme-plugin.spec.ts index cb0a487..91a8291 100644 --- a/lib/uri-schemes/file-scheme-plugin.spec.ts +++ b/lib/uri-schemes/file-scheme-plugin.spec.ts @@ -2,7 +2,7 @@ import { mkdir, rm, writeFile, symlink } from "node:fs/promises"; import { cwd } from "node:process"; import { join } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; -import { describe, it, beforeEach, afterEach, expect } from "vitest"; +import { describe, test, beforeEach, afterEach, expect } from "vitest"; import { MockAgent, setGlobalDispatcher } from "undici"; import { get, RetrievalError } from "../index.js"; import { Reference } from "../jref/index.js"; @@ -25,7 +25,7 @@ describe("JSON Browser", () => { await rm(testPath, { recursive: true, force: true }); }); - it("not found", async () => { + test("not found", async () => { try { await get("nothing-here.jref"); expect.fail("Expected Error: ENOENT: no such file or directory"); @@ -35,7 +35,7 @@ describe("JSON Browser", () => { } }); - it("unknown content type", async () => { + test("unknown content type", async () => { const path = `${fixtureDirectory}/foo.yaml`; const jref = ` foo: 42 @@ -52,7 +52,7 @@ foo: 42 } }); - it("/.jref is not a JRef file", async () => { + test("/.jref is not a JRef file", async () => { const path = `${fixtureDirectory}/.jref`; const jref = ` foo: 42 @@ -89,7 +89,7 @@ foo: 42 await mockAgent.close(); }); - it("file references not allowed from non-filesystem context", async () => { + test("file references not allowed from non-filesystem context", async () => { try { await get(`${testUri}/foo.jref`, browser); expect.fail("Expected Error: Accessing a file from a non-filesystem document is not allowed"); @@ -98,7 +98,7 @@ foo: 42 } }); - it("file references are allowed from file context", async () => { + test("file references are allowed from file context", async () => { const rootPath = `${fixtureDirectory}/root.jref`; const fooPath = `${fixtureDirectory}/foo.jref`; const jref = "{}"; @@ -117,7 +117,7 @@ foo: 42 }); describe("success", () => { - it("cwd relative path", async () => { + test("cwd relative path", async () => { const path = `${fixtureDirectory}/foo.jref`; const fragment = "/foo"; const href = "#/foo"; @@ -136,7 +136,7 @@ foo: 42 expect(browser.document.root).to.eql({ foo: 42, bar: new Reference(href) }); }); - it("full path", async () => { + test("full path", async () => { const path = fileURLToPath(`${testUri}/${fixtureDirectory}/foo.jref`, { windows: false }); const fragment = "/foo"; const href = "#/foo"; @@ -156,7 +156,7 @@ foo: 42 expect(browser.document.root).to.eql({ foo: 42, bar: new Reference(href) }); }); - it("full URI with authority", async () => { + test("full URI with authority", async () => { const path = fileURLToPath(`${testUri}/${fixtureDirectory}/foo.jref`, { windows: false }); const fragment = "/foo"; const href = "#/foo"; @@ -176,7 +176,7 @@ foo: 42 expect(browser.document.root).to.eql({ foo: 42, bar: new Reference(href) }); }); - it("full URI without authority", async () => { + test("full URI without authority", async () => { const path = fileURLToPath(`${testUri}/${fixtureDirectory}/foo.jref`); const fragment = "/foo"; const href = "#/foo"; @@ -196,7 +196,7 @@ foo: 42 }); }); - it("symlink", async () => { + test("symlink", async () => { const path = `${fixtureDirectory}/symlink-foo.jref`; const actualPath = `${fixtureDirectory}/foo.jref`; const fragment = "/foo"; diff --git a/lib/uri-schemes/http-scheme-plugin.spec.ts b/lib/uri-schemes/http-scheme-plugin.spec.ts index 6db5cea..7b67758 100644 --- a/lib/uri-schemes/http-scheme-plugin.spec.ts +++ b/lib/uri-schemes/http-scheme-plugin.spec.ts @@ -1,4 +1,4 @@ -import { describe, it, beforeEach, afterEach, expect } from "vitest"; +import { describe, test, beforeEach, afterEach, expect } from "vitest"; import { MockAgent, setGlobalDispatcher } from "undici"; import { get, RetrievalError } from "../index.js"; import { Reference } from "../jref/index.js"; @@ -20,7 +20,7 @@ describe("JSON Browser", () => { await mockAgent.close(); }); - it("not found", async () => { + test("not found", async () => { const path = "/foo"; mockAgent.get(testDomain) @@ -36,7 +36,7 @@ describe("JSON Browser", () => { } }); - it("no content type", async () => { + test("no content type", async () => { const path = "/foo"; mockAgent.get(testDomain) @@ -52,7 +52,7 @@ describe("JSON Browser", () => { } }); - it("unsupported content type", async () => { + test("unsupported content type", async () => { const path = "/foo"; const yaml = `foo: 42`; const contentType = "application/yaml"; @@ -71,7 +71,7 @@ describe("JSON Browser", () => { }); [200, 203].forEach((status) => { - it(`${status}`, async () => { + test(`${status}`, async () => { const path = "/foo"; const fragment = "/foo"; const href = "/bar"; @@ -94,7 +94,7 @@ describe("JSON Browser", () => { }); [301, 302, 303, 307, 308].forEach((status) => { - it(`${status}`, async () => { + test(`${status}`, async () => { const path = "/foo"; const redirectPath = "/alternate-foo"; const href = "/bar"; @@ -121,7 +121,7 @@ describe("JSON Browser", () => { }); }); - it("redirect without a Location", async () => { + test("redirect without a Location", async () => { const path = "/foo"; const href = "/bar"; const jref = `{ @@ -142,7 +142,7 @@ describe("JSON Browser", () => { } }); - it("Redirect that inherits a fragment", async () => { + test("Redirect that inherits a fragment", async () => { const path = "/foo"; const fragment = "/foo"; const redirectPath = "/alternate-foo"; @@ -169,7 +169,7 @@ describe("JSON Browser", () => { }); // There isn't a way to make this pass in the browser do to limitations of the Fetch spec. - it.skip("redirect with a Location that includes a fragment", async () => { + test.skip("redirect with a Location that includes a fragment", async () => { const path = "/foo"; const redirectPath = "/alternate-foo"; const redirectFragment = "/main"; @@ -200,7 +200,7 @@ describe("JSON Browser", () => { }); // There isn't a way to make this pass in the browser do to limitations of the Fetch spec. - it.skip("redirect whose inherited fragment gets overriden by the Location's fragment", async () => { + test.skip("redirect whose inherited fragment gets overriden by the Location's fragment", async () => { const path = "/foo"; const fragment = "/root"; const redirectPath = "/alternate-foo"; @@ -232,7 +232,7 @@ describe("JSON Browser", () => { }); [201, 202, 204, 205, 206, 300, 304].forEach((status) => { - it(`${status}`, async () => { + test(`${status}`, async () => { const path = "/foo"; mockAgent.get(testDomain) diff --git a/lib/uri-schemes/uri-schemes.spec.ts b/lib/uri-schemes/uri-schemes.spec.ts index 01cade1..876d3e1 100644 --- a/lib/uri-schemes/uri-schemes.spec.ts +++ b/lib/uri-schemes/uri-schemes.spec.ts @@ -1,5 +1,5 @@ import { Readable } from "node:stream"; -import { describe, it, beforeEach, afterEach, expect } from "vitest"; +import { describe, test, beforeEach, afterEach, expect } from "vitest"; import { Response } from "undici"; import { addUriSchemePlugin, removeUriSchemePlugin, get, RetrievalError } from "../index.js"; @@ -8,7 +8,7 @@ describe("JSON Browser", () => { describe("URI schemes", () => { const fixtureDocument = `{"foo": 42}`; - it("Error on unsupported scheme", async () => { + test("Error on unsupported scheme", async () => { try { await get("foo:something"); expect.fail("Expected RetrievalError => UnsupportedUriSchemeError"); @@ -21,7 +21,7 @@ describe("JSON Browser", () => { describe("Custom URI scheme plugin", () => { beforeEach(() => { addUriSchemePlugin("foo", { - retrieve: async (uri) => { + retrieve: async (uri) => { // eslint-disable-line @typescript-eslint/require-await const stream = new Readable(); stream.push(fixtureDocument); stream.push(null); @@ -39,11 +39,11 @@ describe("JSON Browser", () => { removeUriSchemePlugin("application/foo"); }); - it("addUriSchemePlugin", async () => { + test("addUriSchemePlugin", async () => { await get("foo:something"); }); - it("removeUriSchemePlugin", async () => { + test("removeUriSchemePlugin", async () => { removeUriSchemePlugin("foo"); try { diff --git a/package.json b/package.json index 29905c2..2da9195 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "scripts": { "clean": "xargs -a .gitignore rm -rf", "lint": "eslint lib", + "type-check": "tsc --noEmit", "test": "vitest --watch=false" }, "repository": { @@ -35,15 +36,12 @@ "url": "https://github.com/sponsors/jdesrosiers" }, "devDependencies": { + "@stylistic/eslint-plugin": "*", "@types/node": "*", - "@typescript-eslint/eslint-plugin": "*", - "@typescript-eslint/parser": "*", - "eslint": "*", - "eslint-import-resolver-exports": "*", - "eslint-import-resolver-node": "*", "eslint-import-resolver-typescript": "*", "eslint-plugin-import": "*", "typescript": "*", + "typescript-eslint": "*", "undici": "*", "vitest": "*" }, diff --git a/tsconfig.json b/tsconfig.json index 5910e58..7593897 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,9 @@ "moduleResolution": "Node16", "esModuleInterop": true, "strict": true, - "allowJs": true + "strictNullChecks": true, + "allowJs": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true } }