diff --git a/lib/__tests__/pointer.test.ts b/lib/__tests__/pointer.test.ts new file mode 100644 index 00000000..e8d8a718 --- /dev/null +++ b/lib/__tests__/pointer.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest"; +import { $RefParser } from ".."; +import path from "path"; + +describe("pointer", () => { + it("inlines internal JSON Pointer refs under #/paths/ for OpenAPI bundling", async () => { + const refParser = new $RefParser(); + const pathOrUrlOrSchema = path.resolve("lib", "__tests__", "spec", "openapi-paths-ref.json"); + const schema = (await refParser.bundle({ pathOrUrlOrSchema })) as any; + + // The GET endpoint should have its schema defined inline + const getSchema = schema.paths["/foo"].get.responses["200"].content["application/json"].schema; + expect(getSchema.$ref).toBeUndefined(); + expect(getSchema.type).toBe("object"); + expect(getSchema.properties.bar.type).toBe("string"); + + // The POST endpoint should have its schema inlined (copied) instead of a $ref + const postSchema = schema.paths["/foo"].post.responses["200"].content["application/json"].schema; + expect(postSchema.$ref).toBeUndefined(); + expect(postSchema.type).toBe("object"); + expect(postSchema.properties.bar.type).toBe("string"); + + // Both schemas should be identical objects + expect(postSchema).toEqual(getSchema); + }); +}); diff --git a/lib/__tests__/spec/openapi-paths-ref.json b/lib/__tests__/spec/openapi-paths-ref.json new file mode 100644 index 00000000..e3b6bb90 --- /dev/null +++ b/lib/__tests__/spec/openapi-paths-ref.json @@ -0,0 +1,46 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Sample API", + "version": "1.0.0" + }, + "paths": { + "/foo": { + "get": { + "summary": "Get foo", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "bar": { + "type": "string" + } + } + } + } + } + } + } + }, + "post": { + "summary": "Create foo", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/paths/~1foo/get/responses/200/content/application~1json/schema" + } + } + } + } + } + } + } + } +} diff --git a/lib/bundle.ts b/lib/bundle.ts index 4ad729a1..911dcf67 100644 --- a/lib/bundle.ts +++ b/lib/bundle.ts @@ -300,7 +300,7 @@ function remap(inventory: InventoryEntry[]) { for (const entry of inventory) { // console.log('Re-mapping $ref pointer "%s" at %s', entry.$ref.$ref, entry.pathFromRoot); - if (!entry.external) { + if (!entry.external && !entry.hash?.startsWith("#/paths/")) { // This $ref already resolves to the main JSON Schema file entry.$ref.$ref = entry.hash; } else if (entry.file === file && entry.hash === hash) { diff --git a/package.json b/package.json index 675fad54..863d8879 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ ], "scripts": { "build": "rimraf dist && tsc", + "dev": "rimraf dist && tsc --watch", "lint": "eslint lib", "prepublishOnly": "yarn build", "prettier": "prettier --write \"**/*.+(js|jsx|ts|tsx|har||json|css|md)\"",