# Prettier Plugin OpenAPI A Prettier plugin for formatting OpenAPI/Swagger JSON and YAML files with intelligent key sorting, proper indentation, and support for modular OpenAPI file structures. ## Features - ๐ŸŽฏ **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/.yml` and `.json` file 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 ## Current Status โœ… **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 ## Installation ```bash 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 ``` ## Usage ### Command Line ```bash # 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 100 ``` ### Configuration Add the plugin to your Prettier configuration: **package.json** ```json { "prettier": { "plugins": ["prettier-plugin-openapi"] } } ``` **.prettierrc** ```json { "plugins": ["prettier-plugin-openapi"], "tabWidth": 2, "printWidth": 80 } ``` **.prettierrc.js** ```javascript module.exports = { plugins: ['prettier-plugin-openapi'], tabWidth: 2, printWidth: 80, }; ``` ## Supported File Extensions - `.openapi.json` - `.openapi.yaml` - `.openapi.yml` - `.swagger.json` - `.swagger.yaml` - `.swagger.yml` - `.json` (for component files) - `.yaml` / `.yml` (for component files) ## Modular File Structure Support The plugin supports both monolithic and modular OpenAPI file structures: ### Monolithic Structure ``` api.yaml # Single file with everything ``` ### Modular Structure ``` โ”œโ”€ 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 ``` ## Key Sorting 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](./KEYS.md). ## Examples ### Monolithic File Structure #### Before (unformatted): ```yaml paths: /users: get: responses: '200': description: OK components: schemas: User: type: object openapi: 3.0.0 info: version: 1.0.0 title: My API ``` #### After (formatted): ```yaml openapi: 3.0.0 info: title: My API version: 1.0.0 paths: /users: get: responses: '200': description: OK components: schemas: User: type: object ``` ### Modular File Structure #### Root Document (`openapi.yaml`): ```yaml openapi: 3.0.0 info: title: My API version: 1.0.0 paths: $ref: './paths/users.yaml' components: schemas: $ref: './components/schemas/User.yaml' ``` #### Component File (`components/schemas/User.yaml`): ```yaml type: object properties: id: type: integer name: type: string required: - id - name ``` #### Path File (`paths/users.yaml`): ```yaml get: summary: Get users responses: '200': description: Success content: application/json: schema: type: array items: $ref: '../components/schemas/User.yaml' ``` ## Vendor Extension Guide ### Adding Your Vendor Extensions The plugin supports a simple system for vendors to contribute custom extensions. Here's how to add your vendor extensions: #### Step 1: Create Your Vendor Extension File Create a new TypeScript file in `src/extensions/vendor/your-vendor.ts`: ```typescript /** * 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: 'support@your-vendor.com' }, extensions: { // Define your extensions here } }); ``` #### Step 2: Register Your Vendor Add your vendor to the vendor loader in `src/extensions/vendor-loader.ts`: ```typescript // 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 ]; ``` #### Step 3: Define Your Extensions 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: ```typescript 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 }; } } ``` ### ๐ŸŽฏ Enhanced IntelliSense Features The vendor extension system now provides comprehensive IntelliSense support: #### Type-Safe Key Suggestions - **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 #### Available Contexts with IntelliSense - `'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) #### Enhanced Helper Functions ```typescript 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 valid ``` ### Supported Contexts You 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 ### Standard OpenAPI Keys Reference When positioning your extensions, you can reference these standard OpenAPI keys: #### Top-Level Keys - `openapi`, `swagger`, `info`, `externalDocs`, `servers`, `security`, `tags`, `paths`, `webhooks`, `components` #### Info Section Keys - `title`, `version`, `summary`, `description`, `termsOfService`, `contact`, `license` #### Operation Keys - `summary`, `operationId`, `description`, `externalDocs`, `tags`, `deprecated`, `security`, `servers`, `parameters`, `requestBody`, `responses`, `callbacks` #### Schema Keys - `$ref`, `title`, `description`, `type`, `format`, `enum`, `default`, `example`, `properties`, `required`, `items`, `allOf`, `anyOf`, `oneOf`, `not` #### Parameter Keys - `name`, `description`, `in`, `required`, `deprecated`, `schema`, `content`, `style`, `explode`, `allowReserved`, `example` #### Response Keys - `description`, `headers`, `content`, `links` #### Security Scheme Keys - `type`, `description`, `name`, `in`, `scheme`, `bearerFormat`, `flows`, `openIdConnectUrl` #### Server Keys - `url`, `description`, `variables` #### Tag Keys - `name`, `description`, `externalDocs` #### External Docs Keys - `description`, `url` #### Webhook Keys - `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](./KEYS.md). ### Positioning Your Extensions Use the helper functions to position your extensions: - `before(key)` - Position before a standard OpenAPI key - `after(key)` - Position after a standard OpenAPI key #### Example: Positioning Extensions ```typescript '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'), }; } ``` ### Extension Naming Convention 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 ### Testing Your Extensions 1. **Build the project**: `bun run build` 2. **Run tests**: `bun test` 3. **Test with real OpenAPI files**: Create test files with your extensions 4. **Verify positioning**: Check that your extensions appear in the correct order ### Extension Collision Detection 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) ``` ### Advanced Type Safety The vendor extension system provides comprehensive TypeScript support: #### Type Definitions ```typescript 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') }; } }; ``` #### Extension Key Validation ```typescript import { isValidExtensionKey } from "../index.js"; // Validate extension keys follow OpenAPI conventions const isValid = isValidExtensionKey('x-my-vendor-extension'); // โœ… true const isInvalid = isValidExtensionKey('my-extension'); // โŒ false ``` #### Context-Specific Helpers ```typescript import { 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 ``` ### Best Practices 1. **Use descriptive extension names** that clearly indicate their purpose 2. **Position extensions logically** relative to related standard keys 3. **Document your extensions** in your vendor documentation 4. **Test thoroughly** with real OpenAPI files 5. **Follow OpenAPI extension conventions** (x-vendor-name format) 6. **Consider extension conflicts** when choosing names 7. **Leverage IntelliSense** for type-safe key positioning 8. **Use helper functions** for additional validation and discovery ### Troubleshooting #### Common Issues **Extension not appearing in formatted output:** - Check that your vendor is registered in `vendor-loader.ts` - Verify your extension keys follow the `x-vendor-name` format - Ensure your positioning functions return valid numbers **Extensions in wrong order:** - Use `before()` and `after()` 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 #### Debug Tips 1. **Enable debug logging**: Set `DEBUG=prettier-plugin-openapi:*` environment variable 2. **Check console output**: Look for collision warnings and error messages 3. **Test with simple extensions**: Start with basic positioning before complex logic 4. **Verify context names**: Ensure you're using the correct context names from the supported list ### Example: Complete Vendor Extension ```typescript /** * 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: 'support@myapi.com' }, 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'), }; } } }); ``` ## Development ### Modern Development Stack 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 ### Quick Start ```bash # 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 format ``` ### Available Scripts - `bun run dev` - Start development mode with TypeScript watch - `bun run build` - Build the project - `bun run test` - Run all tests - `bun run test:coverage` - Run tests with coverage report - `bun run test:watch` - Run tests in watch mode - `bun run lint` - Run Biome linting - `bun run lint:fix` - Fix Biome linting issues automatically - `bun run format` - Format code with Prettier - `bun run format:check` - Check code formatting - `bun run type-check` - Run TypeScript type checking - `bun run validate` - Run all validation checks (type-check, lint, test) - `bun run clean` - Clean build artifacts ### Project Structure ``` 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 ``` ### Test Suite 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 ### CI/CD Pipeline 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 ## Configuration Options 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) ## Advanced Features ### File Detection The plugin intelligently detects OpenAPI files based on: 1. **Content Structure**: Files with OpenAPI-specific keys (`openapi`, `swagger`, `components`, etc.) 2. **Directory Patterns**: Files in OpenAPI-related directories (`components/`, `paths/`, `webhooks/`) 3. **File Extensions**: Standard OpenAPI file extensions ### Key Sorting Algorithm The plugin uses a unified sorting algorithm that: 1. **Prioritizes Standard Keys**: OpenAPI specification keys are sorted first 2. **Handles Custom Extensions**: Vendor extensions are positioned relative to standard keys 3. **Sorts Unknown Keys**: Non-standard keys are sorted alphabetically at the end 4. **Context-Aware**: Different sorting rules for different OpenAPI contexts (operations, schemas, etc.) ### Performance Optimizations - **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 ## Quality & Reliability ### Comprehensive Testing - **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 ### Code Quality - **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 ### CI/CD Pipeline - **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 ## Contributing We welcome contributions! Please follow these steps: 1. **Fork the repository** 2. **Create a feature branch**: `git checkout -b feature/your-feature-name` 3. **Make your changes** with proper TypeScript types 4. **Add tests** for new functionality (aim for 90%+ coverage) 5. **Run the test suite**: ```bash bun test bun run lint bun run format ``` 6. **Ensure all tests pass** (142 tests, 0 failures) 7. **Submit a pull request** with a clear description ### Development Guidelines - **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 ### Release Process - **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 ## License MIT License - see LICENSE file for details. ## Related Projects - [Prettier](https://prettier.io/) - The core formatter - [OpenAPI Specification](https://spec.openapis.org/oas/v3.1.0.html) - The OpenAPI specification - [Swagger](https://swagger.io/) - API development tools