diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed644ba..e5558a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
## unreleased
+## [1.31.0] - 2026-04-12
+
+- refactor: replace stoplight/yaml with yaml (eemeli) (#206)
+
## [1.30.1] - 2026-03-26
- CLI: Fix normalize YAML block scalar newlines (#202)
diff --git a/package-lock.json b/package-lock.json
index e7bf6c4..5efb62e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.30.1",
"license": "MIT",
"dependencies": {
- "api-ref-bundler": "^0.5.0",
+ "api-ref-bundler": "^0.5.1",
"case-anything": "2.1.10",
"jsonpathly": "^3.0.0",
"neotraverse": "^0.6.18",
@@ -1509,9 +1509,9 @@
}
},
"node_modules/anymatch/node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1522,9 +1522,9 @@
}
},
"node_modules/api-ref-bundler": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/api-ref-bundler/-/api-ref-bundler-0.5.0.tgz",
- "integrity": "sha512-jYrKTpw1Pl3AhaYvDm7LiaqBci7DyM+plcbYJNuhH9EIjkty6CvZZntdraF+GfrinUTfx071hj92kiK4tLUWDg==",
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/api-ref-bundler/-/api-ref-bundler-0.5.1.tgz",
+ "integrity": "sha512-g5YOyKvQVDaTpRpDEmRtnAWZbi1Ur/i9qPJRVf6l9wafg+LJuJn6Aze+wI9h+1qpUSgvLE1CGCiOFlrrBwYEyg==",
"license": "MIT",
"dependencies": {
"json-crawl": "0.4.2"
@@ -1663,9 +1663,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3560,9 +3560,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3996,9 +3996,9 @@
}
},
"node_modules/test-exclude/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5463,17 +5463,17 @@
},
"dependencies": {
"picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true
}
}
},
"api-ref-bundler": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/api-ref-bundler/-/api-ref-bundler-0.5.0.tgz",
- "integrity": "sha512-jYrKTpw1Pl3AhaYvDm7LiaqBci7DyM+plcbYJNuhH9EIjkty6CvZZntdraF+GfrinUTfx071hj92kiK4tLUWDg==",
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/api-ref-bundler/-/api-ref-bundler-0.5.1.tgz",
+ "integrity": "sha512-g5YOyKvQVDaTpRpDEmRtnAWZbi1Ur/i9qPJRVf6l9wafg+LJuJn6Aze+wI9h+1qpUSgvLE1CGCiOFlrrBwYEyg==",
"requires": {
"json-crawl": "0.4.2"
}
@@ -5570,9 +5570,9 @@
"dev": true
},
"brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
@@ -6855,9 +6855,9 @@
"dev": true
},
"picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true
},
"pirates": {
@@ -7137,9 +7137,9 @@
},
"dependencies": {
"brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
diff --git a/package.json b/package.json
index 9b2115b..29a86e5 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
},
"dependencies": {
"yaml": "^2.8.3",
- "api-ref-bundler": "^0.5.0",
+ "api-ref-bundler": "^0.5.1",
"case-anything": "2.1.10",
"jsonpathly": "^3.0.0",
"neotraverse": "^0.6.18"
diff --git a/readme.md b/readme.md
index 67d487b..fbf69e8 100644
--- a/readme.md
+++ b/readme.md
@@ -16,7 +16,7 @@ To quickly standardize OpenAPI documents there is support for generating the ope
The CLI can split large OpenAPI documents into modular, multi-file structures for easier management.
For upgrades, the openapi-format CLI offers the option to convert an OpenAPI 3.0 or 3.1 document to OpenAPI 3.1 or 3.2.
-With the newly added OpenAPI Overlay support, users can overlay changes onto existing OpenAPI documents, to extend and customize the OpenAPI document.
+With the OpenAPI Overlay support, users can overlay changes onto existing OpenAPI documents, to extend and customize the OpenAPI document.
## Table of content
* [Use-cases](#use-cases)
@@ -67,7 +67,6 @@ Postman collections, test suites, ...
- [x] Order OpenAPI fields in a default order
- [x] Order OpenAPI fields in a custom order
- [x] Order Components elements by alphabet
-- [x] Format the casing (camelCase,PascalCase, ...) of component elements
- [x] Filter OpenAPI files based on methods
- [x] Filter OpenAPI files based on flags
- [x] Filter OpenAPI files based on flags values
@@ -75,7 +74,8 @@ Postman collections, test suites, ...
- [x] Filter OpenAPI files based on operationID's
- [x] Filter OpenAPI files based on operations definition
- [x] Filter OpenAPI files based on response content-types
-- [x] Apply OpenAPI overlay actions
+- [x] Format the casing (camelCase,PascalCase, ...) of component elements
+- [x] Apply OpenAPI Overlay actions
- [x] Strip flags from OpenAPI files
- [x] Strip unused components from OpenAPI files
- [x] Generate OpenAPI elements for consistency
@@ -89,7 +89,6 @@ Postman collections, test suites, ...
- [x] Format via CLI
- [x] Format via local or remote config files
- [x] Use as a Module
-- [x] Aligned YAML parsing style with Stoplight Studio style
- [x] Support for OpenAPI 3.0
- [x] Support for OpenAPI 3.1
- [x] Support for OpenAPI 3.2
@@ -112,38 +111,29 @@ More info about the playground features and usage can be found on the [website](
## Installation
-### Local Installation (recommended)
+### NPX usage (recommended)
-While possible to install globally, we recommend that you add the openapi-format CLI to the `node_modules` by using:
+To execute the CLI without installing it via npm, use the npx method
```shell
-$ npm install --save openapi-format
+$ npx openapi-format your-openapi-file.yaml
```
-or using yarn...
+### Local Installation
+
+While possible to install globally, we recommend that you add the openapi-format CLI to the `node_modules` by using:
```shell
-$ yarn add openapi-format
+$ npm install --save openapi-format
```
-Note that this will require you to run the openapi-format CLI with `npx openapi-format your-openapi-file.yaml` or, if
-you are using an older versions of npm, `./node_modules/.bin/openapi-format your-openapi-file.yaml`.
-
### Global Installation
```shell
$ npm install -g openapi-format
```
-### NPX usage
-
-To execute the CLI without installing it via npm, use the npx method
-
-```shell
-$ npx openapi-format your-openapi-file.yaml
-```
-
-### Codex skill usage
+### AI (OpenAI Codex/Claude Code/...) skill usage
To install the `openapi-format` skill from this repository:
@@ -151,7 +141,7 @@ To install the `openapi-format` skill from this repository:
$ npx skills add https://github.com/thim81/openapi-format --skill openapi-format
```
-Then use it in Codex by explicitly referencing the skill in your prompt, for example:
+Then use it in Claude, Codex by explicitly referencing the skill in your prompt, for example:
```text
Using $openapi-format, create a minimal filter config to remove internal endpoints and give me the CLI command.
@@ -243,22 +233,22 @@ the [defaultSort.json](https://github.com/thim81/openapi-format/blob/main/defaul
You can easily modify this by specifying your own ordering per key, which can be passed on to the CLI (see below for an
example on how to do this).
-| Key | Ordered by | OpenAPI reference |
-| ----------- | ----------------------------------------------------------------------------------------------------------------| ------------------------------------------------------------------------- |
-| root | - openapi
\- info
\- servers
\- paths
\- components
\- tags
\- x-tagGroups
\- externalDocs | [openapi-object](https://spec.openapis.org/oas/v3.0.3.html#openapi-object) |
-| get | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
-| post | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
-| put | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
-| patch | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
-| delete | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
-| parameters | - name
\- in
\- description
\- required
\- schema | [parameterObject](https://spec.openapis.org/oas/v3.0.3.html#parameterObject) |
-| requestBody | - description
\- headers
\- content
\- links | [request-body-object](https://spec.openapis.org/oas/v3.0.3.html#request-body-object) |
-| responses | - description
\- headers
\- content
\- links | [responses-object](https://spec.openapis.org/oas/v3.0.3.html#responses-object) |
-| content | (By alphabet) | [responses-object](https://spec.openapis.org/oas/v3.0.3.html#responses-object) |
-| components | - parameters
\- schemas | [components-object](https://spec.openapis.org/oas/v3.0.3.html#components-object) |
-| schema | - description
\- type
\- items
\- properties
\- format
\- example
\- default | [schemaObject](https://spec.openapis.org/oas/v3.0.3.html#schemaObject) |
-| schemas | - description
\- type
\- items
\- properties
\- format
\- example
\- default | |
-| properties | - description
\- type
\- items
\- format
\- example
\- default
\- enum | |
+| Key | Ordered by | OpenAPI reference |
+|-------------|-----------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
+| root | - openapi
\- info
\- servers
\- paths
\- components
\- tags
\- x-tagGroups
\- externalDocs | [openapi-object](https://spec.openapis.org/oas/v3.0.3.html#openapi-object) |
+| get | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
+| post | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
+| put | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
+| patch | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
+| delete | - operationId
\- summary
\- description
\- parameters
\- requestBody
\- responses | [operationObject](https://spec.openapis.org/oas/v3.0.3.html#operationObject) |
+| parameters | - name
\- in
\- description
\- required
\- schema | [parameterObject](https://spec.openapis.org/oas/v3.0.3.html#parameterObject) |
+| requestBody | - description
\- headers
\- content
\- links | [request-body-object](https://spec.openapis.org/oas/v3.0.3.html#request-body-object) |
+| responses | - description
\- headers
\- content
\- links | [responses-object](https://spec.openapis.org/oas/v3.0.3.html#responses-object) |
+| content | (By alphabet) | [responses-object](https://spec.openapis.org/oas/v3.0.3.html#responses-object) |
+| components | - parameters
\- schemas | [components-object](https://spec.openapis.org/oas/v3.0.3.html#components-object) |
+| schema | - description
\- type
\- items
\- properties
\- format
\- example
\- default | [schemaObject](https://spec.openapis.org/oas/v3.0.3.html#schemaObject) |
+| schemas | - description
\- type
\- items
\- properties
\- format
\- example
\- default | |
+| properties | - description
\- type
\- items
\- format
\- example
\- default
\- enum | |
Have a look at the folder [yaml-default](test/yaml-default) and compare the "output.yaml" (sorted document) with the "input.yaml" (original document), to see how openapi-format have sorted the OpenAPI document.
@@ -1699,11 +1689,19 @@ For handling AsyncAPI documents, we have created a separate
package [asyncapi-format](https://github.com/thim81/asyncapi-format) to allow customisation specific for AsyncAPI
use-cases.
+## YAML handling
+
+As of `v1.31.0`, `openapi-format` uses `yaml` by [eemeli](https://github.com/eemeli/yaml) instead of `@stoplight/yaml`, thanks to the contribution of [@guilhas07](https://github.com/guilhas07).
+
+This improves YAML comment preservation and fixes previous formatting issues where:
+- valid YAML strings were unnecessarily quoted
+- double-quoted strings were converted to single quotes
+
## Credits
This package is inspired by
the [@microsoft.azure/format-spec](https://www.npmjs.com/package/@microsoft.azure/format-spec) from [@fearthecowboy](https://github.com/fearthecowboy). The
-original code was not available on GitHub, with the last update being 3 years ago, so to improve support and extend it we
+original code was not available on GitHub, with the last update since 2018, so to improve support and extend it; we
tried to reproduce the original functionality.
The filter capabilities from `openapi-format` are inspired by the work from [@MikeRalphson](https://github.com/mikeralphson) on
diff --git a/test/util-file.test.js b/test/util-file.test.js
index 642dd9e..1171ed4 100644
--- a/test/util-file.test.js
+++ b/test/util-file.test.js
@@ -396,7 +396,7 @@ describe('openapi-format CLI file tests', () => {
});
describe('addQuotesToRefInString function', () => {
- test('should add \' quotes to $ref in string', () => {
+ test("should add ' quotes to $ref in string", () => {
const input = '$ref: #/components/schemas/Example';
const output = addQuotesToRefInString(input);
expect(output).toBe("$ref: '#/components/schemas/Example'");
@@ -419,6 +419,40 @@ describe('openapi-format CLI file tests', () => {
const output = addQuotesToRefInString(input);
expect(output).toBe(input);
});
+
+ test('should parse unquoted local $ref values that start with #', async () => {
+ const yamlString = 'schema:\n $ref: #/components/schemas/Example';
+ const result = await parseString(yamlString);
+
+ expect(result).toEqual({
+ schema: {
+ $ref: '#/components/schemas/Example'
+ }
+ });
+ });
+
+ test('should parse unquoted external $ref values with fragments', async () => {
+ const yamlString = 'schema:\n $ref: ./common.yaml#/components/schemas/Example';
+ const result = await parseString(yamlString);
+
+ expect(result).toEqual({
+ schema: {
+ $ref: './common.yaml#/components/schemas/Example'
+ }
+ });
+ });
+
+ test('should stringify $ref values with quotes in YAML output', async () => {
+ const obj = {
+ schema: {
+ $ref: '#/components/schemas/Example'
+ }
+ };
+
+ const result = await stringify(obj, {format: 'yaml'});
+
+ expect(result).toContain("$ref: '#/components/schemas/Example'");
+ });
});
describe('analyzeOpenApi function', () => {
@@ -486,4 +520,4 @@ describe('openapi-format CLI file tests', () => {
});
});
});
-});
\ No newline at end of file
+});