A Prettier plugin for formatting OpenAPI/Swagger JSON and YAML files with intelligent key sorting, proper indentation, and support for modular OpenAPI file structures.
- 🎯 OpenAPI/Swagger Support: Formats both JSON and YAML OpenAPI specifications
- 🔄 Smart Key Sorting: Automatically sorts OpenAPI keys in the recommended order
- 📁 Modular File Support: Handles both monolithic and modular OpenAPI file structures
- 🧩 Component Files: Supports individual component files (schemas, parameters, responses, etc.)
- 📝 YAML & JSON: Supports both
.yaml/.ymland.jsonfile formats - 🎨 Consistent Formatting: Applies consistent indentation and line breaks
- 🔌 Vendor Extensions: Programmatic loading of vendor-specific extensions
- ⚡ Fast: Built with performance in mind using modern JavaScript
- 🧪 Comprehensive Testing: 142 tests with 95.69% line coverage
- 🚀 CI/CD Ready: Automated testing, building, and publishing
- 🔒 Strict Validation: Properly rejects non-OpenAPI content
- 📊 High Quality: Biome, Prettier, and TypeScript for code quality
✅ Production Ready: Version 1.0.1 with comprehensive test coverage
✅ Modern Tooling: Updated to use Biome for fast linting and formatting
✅ Comprehensive Testing: 142 tests covering all major functionality
✅ High Performance: Optimized for large OpenAPI files
✅ Active Development: Regular updates and improvements
npm install --save-dev prettier-plugin-openapi
# or
pnpm add --dev prettier-plugin-openapi
# or
yarn add --dev prettier-plugin-openapi
# or
bun add --dev prettier-plugin-openapi# Format a single file
pnpx prettier --write api.yaml
# Format all OpenAPI files in a directory
pnpx prettier --write "**/*.{openapi.json,openapi.yaml,swagger.json,swagger.yaml}"
# Format with specific options
pnpx prettier --write api.yaml --tab-width 4 --print-width 100Add the plugin to your Prettier configuration:
package.json
{
"prettier": {
"plugins": ["prettier-plugin-openapi"]
}
}.prettierrc
{
"plugins": ["prettier-plugin-openapi"],
"tabWidth": 2,
"printWidth": 80
}.prettierrc.js
module.exports = {
plugins: ['prettier-plugin-openapi'],
tabWidth: 2,
printWidth: 80,
};.openapi.json.openapi.yaml.openapi.yml.swagger.json.swagger.yaml.swagger.yml.json(for component files).yaml/.yml(for component files)
The plugin supports both monolithic and modular OpenAPI file structures:
api.yaml # Single file with everything
├─ openapi.yaml # Root document
├─ paths/ # Path files
│ ├─ users.yaml
│ ├─ users_{id}.yaml
│ └─ auth_login.yaml
├─ components/ # Component files
│ ├─ schemas/
│ │ ├─ User.yaml
│ │ ├─ UserCreate.yaml
│ │ └─ Error.yaml
│ ├─ parameters/
│ │ ├─ CommonPagination.yaml
│ │ └─ UserId.yaml
│ ├─ responses/
│ │ ├─ ErrorResponse.yaml
│ │ └─ UserResponse.yaml
│ ├─ requestBodies/
│ │ └─ UserCreateBody.yaml
│ ├─ headers/
│ │ └─ RateLimitHeaders.yaml
│ ├─ examples/
│ │ └─ UserExample.yaml
│ ├─ securitySchemes/
│ │ └─ BearerAuth.yaml
│ ├─ links/
│ │ └─ UserCreatedLink.yaml
│ └─ callbacks/
│ └─ NewMessageCallback.yaml
└─ webhooks/ # Webhook files
└─ messageCreated.yaml
The plugin automatically sorts OpenAPI keys in the recommended order:
📖 Complete Key Reference: For a comprehensive reference of all keys, their ordering, and detailed reasoning, see KEYS.md.
paths:
/users:
get:
responses:
'200':
description: OK
components:
schemas:
User:
type: object
openapi: 3.0.0
info:
version: 1.0.0
title: My APIopenapi: 3.0.0
info:
title: My API
version: 1.0.0
paths:
/users:
get:
responses:
'200':
description: OK
components:
schemas:
User:
type: objectopenapi: 3.0.0
info:
title: My API
version: 1.0.0
paths:
$ref: './paths/users.yaml'
components:
schemas:
$ref: './components/schemas/User.yaml'type: object
properties:
id:
type: integer
name:
type: string
required:
- id
- nameget:
summary: Get users
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '../components/schemas/User.yaml'The plugin supports a simple system for vendors to contribute custom extensions.
Here's how to add your vendor extensions:
Create a new TypeScript file in src/extensions/vendor/your-vendor.ts:
/**
* Your Vendor Extensions
*
* Your vendor extensions for OpenAPI formatting.
* Website: https://your-vendor.com
*/
import { defineConfig } from "../index.js";
export const yourVendor = defineConfig({
info: {
name: 'Your Vendor',
website: 'https://your-vendor.com',
support: '[email protected]'
},
extensions: {
// Define your extensions here
}
});Add your vendor to the vendor loader in src/extensions/vendor-loader.ts:
// Import your vendor extension
import { yourVendor } from './vendor/your-vendor.js';
// Add to the vendorModules array
const vendorModules = [
speakeasy,
postman,
redoc,
yourVendor // Add your vendor here
];Use the before() and after() helper functions to position your extensions relative to standard OpenAPI keys. The system now provides full IntelliSense support with type-safe key suggestions:
extensions: {
'top-level': (before, after) => {
// ✅ IntelliSense shows: 'swagger', 'openapi', 'info', 'paths', etc.
return {
'x-your-vendor-sdk': before('info'), // ✅ Type-safe: 'info' is valid
'x-your-vendor-auth': after('paths'), // ✅ Type-safe: 'paths' is valid
// 'x-invalid': before('invalidKey'), // ❌ TypeScript error: 'invalidKey' not valid
};
},
'operation': (before, after) => {
// ✅ IntelliSense shows: 'summary', 'operationId', 'parameters', 'responses', etc.
return {
'x-your-vendor-retries': after('parameters'), // ✅ Type-safe: 'parameters' is valid
'x-your-vendor-timeout': before('responses'), // ✅ Type-safe: 'responses' is valid
};
},
'schema': (before, after) => {
// ✅ IntelliSense shows: '$ref', 'title', 'type', 'format', 'example', etc.
return {
'x-your-vendor-validation': after('type'), // ✅ Type-safe: 'type' is valid
'x-your-vendor-example': after('example'), // ✅ Type-safe: 'example' is valid
};
}
}The vendor extension system now provides comprehensive IntelliSense support:
- Context-aware autocomplete: Each context shows only valid OpenAPI keys
- Real-time validation: TypeScript errors for invalid keys
- Hover documentation: Detailed information about each key's purpose
'top-level'→ Shows:swagger,openapi,info,paths,components, etc.'info'→ Shows:title,version,description,contact,license, etc.'operation'→ Shows:summary,operationId,parameters,responses, etc.'schema'→ Shows:$ref,title,type,format,example, etc.'parameter'→ Shows:name,description,in,required,schema, etc.'response'→ Shows:description,headers,content,links'securityScheme'→ Shows:type,description,name,in,scheme, etc.'server'→ Shows:url,description,variables'tag'→ Shows:name,description,externalDocs'externalDocs'→ Shows:description,url'webhook'→ Shows:summary,operationId,parameters,responses, etc.'definitions'→ Shows schema keys (Swagger 2.0)'securityDefinitions'→ Shows security scheme keys (Swagger 2.0)
import { createPositionHelpers } from "../index.js";
// Get enhanced helpers for a specific context
const helpers = createPositionHelpers('operation');
// Type-safe positioning
helpers.before('parameters'); // ✅ IntelliSense shows valid operation keys
helpers.after('responses'); // ✅ IntelliSense shows valid operation keys
// Additional utilities
const availableKeys = helpers.getAvailableKeys(); // Get all valid keys
const isValid = helpers.isValidKey('summary'); // Check if key is validYou can define extensions for these OpenAPI contexts:
'top-level'- Root OpenAPI document'info'- API information section'operation'- HTTP operations (GET, POST, etc.)'parameter'- Operation parameters'schema'- Data schemas'response'- Operation responses'securityScheme'- Security schemes'server'- Server definitions'tag'- API tags'externalDocs'- External documentation'webhook'- Webhook definitions'definitions'- Swagger 2.0 definitions'securityDefinitions'- Swagger 2.0 security definitions
When positioning your extensions, you can reference these standard OpenAPI keys:
openapi,swagger,info,externalDocs,servers,security,tags,paths,webhooks,components
title,version,summary,description,termsOfService,contact,license
summary,operationId,description,externalDocs,tags,deprecated,security,servers,parameters,requestBody,responses,callbacks
$ref,title,description,type,format,enum,default,example,properties,required,items,allOf,anyOf,oneOf,not
name,description,in,required,deprecated,schema,content,style,explode,allowReserved,example
description,headers,content,links
type,description,name,in,scheme,bearerFormat,flows,openIdConnectUrl
url,description,variables
name,description,externalDocs
description,url
summary,operationId,description,deprecated,tags,security,servers,parameters,requestBody,responses,callbacks
📖 Complete Key Reference: For a comprehensive reference of all keys, their ordering, and detailed reasoning, see KEYS.md.
Use the helper functions to position your extensions:
before(key)- Position before a standard OpenAPI keyafter(key)- Position after a standard OpenAPI key
'operation': (before, after) => {
return {
// Position before standard keys
'x-your-vendor-auth': before('security'),
'x-your-vendor-rate-limit': before('parameters'),
// Position after standard keys
'x-your-vendor-retries': after('parameters'),
'x-your-vendor-timeout': after('responses'),
// Position relative to other extensions
'x-your-vendor-cache': after('x-your-vendor-retries'),
};
}Follow these naming conventions for your extensions:
- Use your vendor prefix:
x-your-vendor- - Use descriptive names:
x-your-vendor-retries,x-your-vendor-timeout - Keep names consistent across contexts
- Use kebab-case for multi-word extensions
- Build the project:
bun run build - Run tests:
bun test - Test with real OpenAPI files: Create test files with your extensions
- Verify positioning: Check that your extensions appear in the correct order
The system automatically detects and warns about extension key collisions:
⚠️ Extension collision detected!
Key: "x-common-extension" in context "operation"
Already defined by: Vendor A
Conflicting with: Vendor B
Using position from: Vendor A (5)
Ignoring position from: Vendor B (3)
The vendor extension system provides comprehensive TypeScript support:
import {
type VendorExtensions,
type ContextExtensionFunction,
type OpenAPIContext,
type ExtensionKey
} from "../index.js";
// Type-safe extension configuration
const extensions: VendorExtensions = {
'top-level': (before, after) => {
// before and after are type-safe for top-level keys
return {
'x-my-extension': before('info'),
'x-my-config': after('paths')
};
}
};import { isValidExtensionKey } from "../index.js";
// Validate extension keys follow OpenAPI conventions
const isValid = isValidExtensionKey('x-my-vendor-extension'); // ✅ true
const isInvalid = isValidExtensionKey('my-extension'); // ❌ falseimport { createPositionHelpers } from "../index.js";
// Get type-safe helpers for a specific context
const operationHelpers = createPositionHelpers('operation');
// All functions are type-safe
operationHelpers.before('summary'); // ✅ Valid operation key
operationHelpers.after('responses'); // ✅ Valid operation key
operationHelpers.isValidKey('summary'); // ✅ true
operationHelpers.getAvailableKeys(); // Returns all valid operation keys- Use descriptive extension names that clearly indicate their purpose
- Position extensions logically relative to related standard keys
- Document your extensions in your vendor documentation
- Test thoroughly with real OpenAPI files
- Follow OpenAPI extension conventions (x-vendor-name format)
- Consider extension conflicts when choosing names
- Leverage IntelliSense for type-safe key positioning
- Use helper functions for additional validation and discovery
Extension not appearing in formatted output:
- Check that your vendor is registered in
vendor-loader.ts - Verify your extension keys follow the
x-vendor-nameformat - Ensure your positioning functions return valid numbers
Extensions in wrong order:
- Use
before()andafter()helper functions for positioning - Check that referenced standard keys exist in the context
- Verify your positioning logic is correct
Extension collisions:
- Use unique vendor prefixes to avoid conflicts
- Check the console for collision warnings
- Consider renaming conflicting extensions
Build errors:
- Ensure your TypeScript syntax is correct
- Check that all imports are properly resolved
- Verify your extension structure matches the expected format
- Enable debug logging: Set
DEBUG=prettier-plugin-openapi:*environment variable - Check console output: Look for collision warnings and error messages
- Test with simple extensions: Start with basic positioning before complex logic
- Verify context names: Ensure you're using the correct context names from the supported list
/**
* MyAPI Extensions
*
* MyAPI platform extensions for OpenAPI formatting.
* Website: https://myapi.com
*/
import { defineConfig } from "../index.js";
export const myapi = defineConfig({
info: {
name: 'MyAPI',
website: 'https://myapi.com',
support: '[email protected]'
},
extensions: {
'top-level': (before, after) => {
return {
'x-myapi-sdk': before('info'),
'x-myapi-version': after('info'),
};
},
'operation': (before, after) => {
return {
'x-myapi-rate-limit': before('parameters'),
'x-myapi-retries': after('parameters'),
'x-myapi-timeout': after('responses'),
};
},
'schema': (before, after) => {
return {
'x-myapi-validation': after('type'),
'x-myapi-example': after('example'),
};
}
}
});This project uses modern development tools for optimal performance and developer experience:
- Bun - Fast JavaScript runtime and package manager
- TypeScript - Type-safe development with strict settings
- Biome - Fast linting and formatting (replaces ESLint + Prettier for code)
- Prettier - Documentation and configuration file formatting
- GitHub Actions - Automated CI/CD with smart releases
# Install dependencies
bun install
# Build the plugin
bun run build
# Run tests
bun test
# Run tests with coverage
bun test --coverage
# Lint code
bun run lint
# Fix linting issues
bun run lint:fix
# Format code
bun run formatbun run dev- Start development mode with TypeScript watchbun run build- Build the projectbun run test- Run all testsbun run test:coverage- Run tests with coverage reportbun run test:watch- Run tests in watch modebun run lint- Run Biome lintingbun run lint:fix- Fix Biome linting issues automaticallybun run format- Format code with Prettierbun run format:check- Check code formattingbun run type-check- Run TypeScript type checkingbun run validate- Run all validation checks (type-check, lint, test)bun run clean- Clean build artifacts
src/
index.ts # Main plugin implementation
keys.ts # OpenAPI key definitions
extensions/
index.ts # Extension system
vendor-loader.ts # Automatic vendor loading
vendor/ # Vendor extensions
speakeasy.ts
postman.ts
redoc.ts
test/
plugin.test.ts # Core plugin tests
integration.test.ts # Integration tests
build.test.ts # Build validation tests
coverage.test.ts # Coverage enhancement tests
file-detection.test.ts # File detection tests
key-ordering.test.ts # Key sorting tests
custom-extensions.test.ts # Extension tests
options.test.ts # Configuration tests
simple-ordering.test.ts # Basic ordering tests
vendor.test.ts # Vendor extension tests
setup.ts # Test utilities
The project includes a comprehensive test suite with 142 tests covering:
- Core Functionality: Plugin structure, parsing, formatting
- Integration Tests: Real OpenAPI file processing, error handling
- Build Tests: Package validation, TypeScript compilation
- Coverage Tests: Edge cases, error scenarios
- File Detection: OpenAPI file recognition, component files
- Key Ordering: OpenAPI key sorting, custom extensions
- Vendor Extensions: Extension system functionality
- Options: Configuration and formatting options
Coverage: 95.69% line coverage, 97.00% function coverage
The project includes automated CI/CD with GitHub Actions:
- Continuous Integration: Tests on Node.js 18, 20, 22 and Bun
- Automated Testing: Linting with Biome, type checking, security audits
- Smart Releases: Automatic patch version bumps on main branch updates
- NPM Publishing: Automated publishing with version management
- Quality Gates: All tests must pass before release
The plugin respects standard Prettier options:
tabWidth: Number of spaces for indentation (default: 2)printWidth: Maximum line length (default: 80)useTabs: Use tabs instead of spaces (default: true)
The plugin intelligently detects OpenAPI files based on:
- Content Structure: Files with OpenAPI-specific keys (
openapi,swagger,components, etc.) - Directory Patterns: Files in OpenAPI-related directories (
components/,paths/,webhooks/) - File Extensions: Standard OpenAPI file extensions
The plugin uses a unified sorting algorithm that:
- Prioritizes Standard Keys: OpenAPI specification keys are sorted first
- Handles Custom Extensions: Vendor extensions are positioned relative to standard keys
- Sorts Unknown Keys: Non-standard keys are sorted alphabetically at the end
- Context-Aware: Different sorting rules for different OpenAPI contexts (operations, schemas, etc.)
- Unified Sorting Function: Single function handles all sorting scenarios
- Lazy Loading: Vendor extensions are loaded only when needed
- Efficient Detection: Fast file type detection with minimal overhead
- 142 Test Cases: Covering all major functionality
- 95.69% Line Coverage: Extensive test coverage
- 97.00% Function Coverage: Nearly complete function testing
- Edge Case Testing: Malformed files, error scenarios, performance
- Integration Testing: Real-world OpenAPI file processing
- TypeScript: Full type safety and IntelliSense support
- Biome: Fast linting and formatting with TypeScript support
- Prettier: Consistent code formatting
- Security Audits: Automated dependency vulnerability scanning
- Performance Testing: Large file handling and memory usage
- Automated Testing: Runs on every commit and PR
- Multi-Environment: Tests on Node.js 18, 20, 22 and Bun
- Quality Gates: All tests must pass before merge
- Smart Releases: Automatic patch version management
- NPM Publishing: Automated package publishing with proper versioning
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes with proper TypeScript types
- Add tests for new functionality (aim for 90%+ coverage)
- Run the test suite:
bun test bun run lint bun run format - Ensure all tests pass (142 tests, 0 failures)
- Submit a pull request with a clear description
- Code Quality: All code must pass Biome and Prettier checks
- Testing: New features require comprehensive tests
- TypeScript: Use proper types and interfaces
- Documentation: Update README for new features
- CI/CD: All GitHub Actions must pass before merge
- Automatic: Patch releases happen automatically on main branch updates
- Manual: Major/minor releases require manual version bumps
- Quality Gates: All tests, linting, and security checks must pass
- NPM Publishing: Automated publishing with proper versioning
MIT License - see LICENSE file for details.
- Prettier - The core formatter
- OpenAPI Specification - The OpenAPI specification
- Swagger - API development tools