Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 51 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
Install using [npm](https://docs.npmjs.com/about-npm/):

```bash
npm install @apidevtools/json-schema-ref-parser
yarn add @apidevtools/json-schema-ref-parser
bun add @apidevtools/json-schema-ref-parser
npm install @hey-api/json-schema-ref-parser
yarn add @hey-api/json-schema-ref-parser
bun add @hey-api/json-schema-ref-parser
```

## The Problem:
Expand Down Expand Up @@ -69,21 +69,61 @@ JavaScript objects.
## Example

```javascript
import $RefParser from "@apidevtools/json-schema-ref-parser";
import { $RefParser } from "@hey-api/json-schema-ref-parser";

try {
await $RefParser.dereference(mySchema);
// note - by default, mySchema is modified in place, and the returned value is a reference to the same object
console.log(mySchema.definitions.person.properties.firstName);

// if you want to avoid modifying the original schema, you can disable the `mutateInputSchema` option
let clonedSchema = await $RefParser.dereference(mySchema, { mutateInputSchema: false });
console.log(clonedSchema.definitions.person.properties.firstName);
const parser = new $RefParser();
await parser.dereference({ pathOrUrlOrSchema: mySchema });
console.log(parser.schema.definitions.person.properties.firstName);
} catch (err) {
console.error(err);
}
```

### New in this fork (@hey-api)

- **Multiple inputs with `bundleMany`**: Merge and bundle several OpenAPI/JSON Schema inputs (files, URLs, or raw objects) into a single schema. Components are prefixed to avoid name collisions, paths are namespaced on conflict, and `$ref`s are rewritten accordingly.

```javascript
import { $RefParser } from "@hey-api/json-schema-ref-parser";

const parser = new $RefParser();
const merged = await parser.bundleMany({
pathOrUrlOrSchemas: [
"./specs/a.yaml",
"https://example.com/b.yaml",
{ openapi: "3.1.0", info: { title: "Inline" }, paths: {} },
],
});

// merged.components.* will contain prefixed names like a_<name>, b_<name>, etc.
```

- **Dereference hooks**: Fine-tune dereferencing with `excludedPathMatcher(path) => boolean` to skip subpaths and `onDereference(path, value, parent, parentPropName)` to observe replacements.

```javascript
const parser = new $RefParser();
parser.options.dereference.excludedPathMatcher = (p) => p.includes("/example/");
parser.options.dereference.onDereference = (p, v) => {
// inspect p / v as needed
};
await parser.dereference({ pathOrUrlOrSchema: "./openapi.yaml" });
```

- **Smart input resolution**: You can pass a file path, URL, or raw schema object. If a raw schema includes `$id`, it is used as the base URL for resolving relative `$ref`s.

```javascript
await new $RefParser().bundle({
pathOrUrlOrSchema: {
$id: "https://api.example.com/openapi.json",
openapi: "3.1.0",
paths: {
"/ping": { get: { responses: { 200: { description: "ok" } } } },
},
},
});
```

## Polyfills

If you are using Node.js < 18, you'll need a polyfill for `fetch`,
Expand Down
16 changes: 15 additions & 1 deletion lib/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,21 @@ function remap(parser: $RefParser, inventory: InventoryEntry[]) {

let defName = namesForPrefix.get(targetKey);
if (!defName) {
const proposed = `${baseName(entry.file)}_${lastToken(entry.hash)}`;
// If the external file is one of the original input sources, prefer its assigned prefix
let proposedBase = baseName(entry.file);
try {
const parserAny: any = parser as any;
if (parserAny && parserAny.sourcePathToPrefix && typeof parserAny.sourcePathToPrefix.get === "function") {
const withoutHash = (entry.file || "").split("#")[0];
const mapped = parserAny.sourcePathToPrefix.get(withoutHash);
if (mapped && typeof mapped === "string") {
proposedBase = mapped;
}
}
} catch {
// Ignore errors
}
const proposed = `${proposedBase}_${lastToken(entry.hash)}`;
defName = uniqueName(container, proposed);
namesForPrefix.set(targetKey, defName);
// Store the resolved value under the container
Expand Down
Loading
Loading