mirror of
https://github.com/LukeHagar/prettier-plugin-openapi.git
synced 2025-12-06 04:21:03 +00:00
Refactor vendor extensions by consolidating and enhancing type safety. Introduce a new common vendor extension and remove outdated examples, improving overall structure and IntelliSense support.
This commit is contained in:
@@ -1,9 +1,4 @@
|
||||
/**
|
||||
* Example Vendor Extensions
|
||||
*/
|
||||
|
||||
import { defineConfig } from './index.js';
|
||||
|
||||
import { defineConfig, createPositionHelpers } from './index.js';
|
||||
|
||||
// Complete vendor configuration with smart positioning
|
||||
export const config = defineConfig({
|
||||
@@ -34,3 +29,139 @@ export const config = defineConfig({
|
||||
}
|
||||
});
|
||||
|
||||
// Example vendor with enhanced type safety
|
||||
export const exampleEnhanced = defineConfig({
|
||||
info: {
|
||||
name: 'Example Enhanced',
|
||||
website: 'https://example.com',
|
||||
support: 'support@example.com'
|
||||
},
|
||||
extensions: {
|
||||
// Type-safe context with IntelliSense for available keys
|
||||
'top-level': (before, after) => {
|
||||
// IntelliSense will show: 'swagger', 'openapi', 'jsonSchemaDialect', 'info', 'externalDocs', etc.
|
||||
return {
|
||||
'x-example-sdk': before('info'), // ✅ Type-safe: 'info' is a valid top-level key
|
||||
'x-example-config': after('paths'), // ✅ Type-safe: 'paths' is a valid top-level key
|
||||
// 'x-example-invalid': before('invalidKey'), // ❌ TypeScript error: 'invalidKey' is not a valid top-level key
|
||||
};
|
||||
},
|
||||
|
||||
'info': (before, after) => {
|
||||
// IntelliSense will show: 'title', 'version', 'summary', 'description', 'termsOfService', etc.
|
||||
return {
|
||||
'x-example-info': after('version'), // ✅ Type-safe: 'version' is a valid info key
|
||||
'x-example-metadata': before('description'), // ✅ Type-safe: 'description' is a valid info key
|
||||
};
|
||||
},
|
||||
|
||||
'operation': (before, after) => {
|
||||
// IntelliSense will show: 'summary', 'operationId', 'description', 'externalDocs', 'tags', etc.
|
||||
return {
|
||||
'x-example-retries': after('parameters'), // ✅ Type-safe: 'parameters' is a valid operation key
|
||||
'x-example-timeout': before('responses'), // ✅ Type-safe: 'responses' is a valid operation key
|
||||
'x-example-cache': after('servers'), // ✅ Type-safe: 'servers' is a valid operation key
|
||||
};
|
||||
},
|
||||
|
||||
'schema': (before, after) => {
|
||||
// IntelliSense will show: '$ref', 'title', 'description', 'type', 'format', 'enum', etc.
|
||||
return {
|
||||
'x-example-validation': after('type'), // ✅ Type-safe: 'type' is a valid schema key
|
||||
'x-example-example': after('example'), // ✅ Type-safe: 'example' is a valid schema key
|
||||
};
|
||||
},
|
||||
|
||||
'parameter': (before, after) => {
|
||||
// IntelliSense will show: 'name', 'description', 'in', 'required', 'deprecated', etc.
|
||||
return {
|
||||
'x-example-param': after('schema'), // ✅ Type-safe: 'schema' is a valid parameter key
|
||||
};
|
||||
},
|
||||
|
||||
'response': (before, after) => {
|
||||
// IntelliSense will show: 'description', 'headers', 'content', 'links'
|
||||
return {
|
||||
'x-example-response': after('description'), // ✅ Type-safe: 'description' is a valid response key
|
||||
};
|
||||
},
|
||||
|
||||
'securityScheme': (before, after) => {
|
||||
// IntelliSense will show: 'type', 'description', 'name', 'in', 'scheme', etc.
|
||||
return {
|
||||
'x-example-auth': after('type'), // ✅ Type-safe: 'type' is a valid security scheme key
|
||||
};
|
||||
},
|
||||
|
||||
'server': (before, after) => {
|
||||
// IntelliSense will show: 'url', 'description', 'variables'
|
||||
return {
|
||||
'x-example-server': after('url'), // ✅ Type-safe: 'url' is a valid server key
|
||||
};
|
||||
},
|
||||
|
||||
'tag': (before, after) => {
|
||||
// IntelliSense will show: 'name', 'description', 'externalDocs'
|
||||
return {
|
||||
'x-example-tag': after('name'), // ✅ Type-safe: 'name' is a valid tag key
|
||||
};
|
||||
},
|
||||
|
||||
'externalDocs': (before, after) => {
|
||||
// IntelliSense will show: 'description', 'url'
|
||||
return {
|
||||
'x-example-docs': after('url'), // ✅ Type-safe: 'url' is a valid external docs key
|
||||
};
|
||||
},
|
||||
|
||||
'webhook': (before, after) => {
|
||||
// IntelliSense will show: 'summary', 'operationId', 'description', 'deprecated', etc.
|
||||
return {
|
||||
'x-example-webhook': after('operationId'), // ✅ Type-safe: 'operationId' is a valid webhook key
|
||||
};
|
||||
},
|
||||
|
||||
'definitions': (before, after) => {
|
||||
// IntelliSense will show schema keys: '$ref', 'title', 'description', 'type', etc.
|
||||
return {
|
||||
'x-example-definition': after('type'), // ✅ Type-safe: 'type' is a valid schema key
|
||||
};
|
||||
},
|
||||
|
||||
'securityDefinitions': (before, after) => {
|
||||
// IntelliSense will show security scheme keys: 'type', 'description', 'name', etc.
|
||||
return {
|
||||
'x-example-security': after('type'), // ✅ Type-safe: 'type' is a valid security scheme key
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Alternative approach using the enhanced helper functions
|
||||
export const exampleWithHelpers = defineConfig({
|
||||
info: {
|
||||
name: 'Example With Helpers',
|
||||
website: 'https://example.com',
|
||||
support: 'support@example.com'
|
||||
},
|
||||
extensions: {
|
||||
'top-level': (before, after) => {
|
||||
// You can also use the enhanced helpers for additional functionality
|
||||
const helpers = createPositionHelpers('top-level');
|
||||
|
||||
// Get all available keys for this context
|
||||
const availableKeys = helpers.getAvailableKeys();
|
||||
console.log('Available top-level keys:', availableKeys);
|
||||
|
||||
// Validate if a key exists
|
||||
if (helpers.isValidKey('info')) {
|
||||
console.log('info is a valid top-level key');
|
||||
}
|
||||
|
||||
return {
|
||||
'x-example-enhanced': before('info'),
|
||||
'x-example-config': after('paths'),
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
import { before, after, type KeyMap, type VendorExtensions } from './index.js';
|
||||
|
||||
// Import vendor extensions statically
|
||||
import { common } from './vendor/common.js';
|
||||
import { speakeasy } from './vendor/speakeasy.js';
|
||||
import { postman } from './vendor/postman.js';
|
||||
import { redoc } from './vendor/redoc.js';
|
||||
|
||||
// Import vendor extensions statically
|
||||
const vendorModules = [
|
||||
// Update this list as new vendors are added
|
||||
speakeasy,
|
||||
postman,
|
||||
redoc
|
||||
common
|
||||
];
|
||||
|
||||
// Type for vendor module
|
||||
|
||||
21
src/extensions/vendor/common.ts
vendored
Normal file
21
src/extensions/vendor/common.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { defineConfig } from "../index.js";
|
||||
|
||||
// Function-based extensions with before/after helpers
|
||||
export const common = defineConfig({
|
||||
info: {
|
||||
name: 'Common',
|
||||
},
|
||||
extensions: {
|
||||
'top-level': (before, after) => {
|
||||
return {
|
||||
'x-tagGroups': after('tags'),
|
||||
};
|
||||
},
|
||||
'operation': (before, after) => {
|
||||
return {
|
||||
'x-badges': after('tags'),
|
||||
'x-codeSamples': after('schemes'),
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
145
src/extensions/vendor/example-enhanced.ts
vendored
145
src/extensions/vendor/example-enhanced.ts
vendored
@@ -1,145 +0,0 @@
|
||||
/**
|
||||
* Enhanced Vendor Extensions Example
|
||||
*
|
||||
* This example demonstrates the improved IntelliSense and type safety
|
||||
* for vendor extensions configuration.
|
||||
*/
|
||||
|
||||
import { defineConfig, createPositionHelpers } from "../index.js";
|
||||
|
||||
// Example vendor with enhanced type safety
|
||||
export const exampleEnhanced = defineConfig({
|
||||
info: {
|
||||
name: 'Example Enhanced',
|
||||
website: 'https://example.com',
|
||||
support: 'support@example.com'
|
||||
},
|
||||
extensions: {
|
||||
// Type-safe context with IntelliSense for available keys
|
||||
'top-level': (before, after) => {
|
||||
// IntelliSense will show: 'swagger', 'openapi', 'jsonSchemaDialect', 'info', 'externalDocs', etc.
|
||||
return {
|
||||
'x-example-sdk': before('info'), // ✅ Type-safe: 'info' is a valid top-level key
|
||||
'x-example-config': after('paths'), // ✅ Type-safe: 'paths' is a valid top-level key
|
||||
// 'x-example-invalid': before('invalidKey'), // ❌ TypeScript error: 'invalidKey' is not a valid top-level key
|
||||
};
|
||||
},
|
||||
|
||||
'info': (before, after) => {
|
||||
// IntelliSense will show: 'title', 'version', 'summary', 'description', 'termsOfService', etc.
|
||||
return {
|
||||
'x-example-info': after('version'), // ✅ Type-safe: 'version' is a valid info key
|
||||
'x-example-metadata': before('description'), // ✅ Type-safe: 'description' is a valid info key
|
||||
};
|
||||
},
|
||||
|
||||
'operation': (before, after) => {
|
||||
// IntelliSense will show: 'summary', 'operationId', 'description', 'externalDocs', 'tags', etc.
|
||||
return {
|
||||
'x-example-retries': after('parameters'), // ✅ Type-safe: 'parameters' is a valid operation key
|
||||
'x-example-timeout': before('responses'), // ✅ Type-safe: 'responses' is a valid operation key
|
||||
'x-example-cache': after('servers'), // ✅ Type-safe: 'servers' is a valid operation key
|
||||
};
|
||||
},
|
||||
|
||||
'schema': (before, after) => {
|
||||
// IntelliSense will show: '$ref', 'title', 'description', 'type', 'format', 'enum', etc.
|
||||
return {
|
||||
'x-example-validation': after('type'), // ✅ Type-safe: 'type' is a valid schema key
|
||||
'x-example-example': after('example'), // ✅ Type-safe: 'example' is a valid schema key
|
||||
};
|
||||
},
|
||||
|
||||
'parameter': (before, after) => {
|
||||
// IntelliSense will show: 'name', 'description', 'in', 'required', 'deprecated', etc.
|
||||
return {
|
||||
'x-example-param': after('schema'), // ✅ Type-safe: 'schema' is a valid parameter key
|
||||
};
|
||||
},
|
||||
|
||||
'response': (before, after) => {
|
||||
// IntelliSense will show: 'description', 'headers', 'content', 'links'
|
||||
return {
|
||||
'x-example-response': after('description'), // ✅ Type-safe: 'description' is a valid response key
|
||||
};
|
||||
},
|
||||
|
||||
'securityScheme': (before, after) => {
|
||||
// IntelliSense will show: 'type', 'description', 'name', 'in', 'scheme', etc.
|
||||
return {
|
||||
'x-example-auth': after('type'), // ✅ Type-safe: 'type' is a valid security scheme key
|
||||
};
|
||||
},
|
||||
|
||||
'server': (before, after) => {
|
||||
// IntelliSense will show: 'url', 'description', 'variables'
|
||||
return {
|
||||
'x-example-server': after('url'), // ✅ Type-safe: 'url' is a valid server key
|
||||
};
|
||||
},
|
||||
|
||||
'tag': (before, after) => {
|
||||
// IntelliSense will show: 'name', 'description', 'externalDocs'
|
||||
return {
|
||||
'x-example-tag': after('name'), // ✅ Type-safe: 'name' is a valid tag key
|
||||
};
|
||||
},
|
||||
|
||||
'externalDocs': (before, after) => {
|
||||
// IntelliSense will show: 'description', 'url'
|
||||
return {
|
||||
'x-example-docs': after('url'), // ✅ Type-safe: 'url' is a valid external docs key
|
||||
};
|
||||
},
|
||||
|
||||
'webhook': (before, after) => {
|
||||
// IntelliSense will show: 'summary', 'operationId', 'description', 'deprecated', etc.
|
||||
return {
|
||||
'x-example-webhook': after('operationId'), // ✅ Type-safe: 'operationId' is a valid webhook key
|
||||
};
|
||||
},
|
||||
|
||||
'definitions': (before, after) => {
|
||||
// IntelliSense will show schema keys: '$ref', 'title', 'description', 'type', etc.
|
||||
return {
|
||||
'x-example-definition': after('type'), // ✅ Type-safe: 'type' is a valid schema key
|
||||
};
|
||||
},
|
||||
|
||||
'securityDefinitions': (before, after) => {
|
||||
// IntelliSense will show security scheme keys: 'type', 'description', 'name', etc.
|
||||
return {
|
||||
'x-example-security': after('type'), // ✅ Type-safe: 'type' is a valid security scheme key
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Alternative approach using the enhanced helper functions
|
||||
export const exampleWithHelpers = defineConfig({
|
||||
info: {
|
||||
name: 'Example With Helpers',
|
||||
website: 'https://example.com',
|
||||
support: 'support@example.com'
|
||||
},
|
||||
extensions: {
|
||||
'top-level': (before, after) => {
|
||||
// You can also use the enhanced helpers for additional functionality
|
||||
const helpers = createPositionHelpers('top-level');
|
||||
|
||||
// Get all available keys for this context
|
||||
const availableKeys = helpers.getAvailableKeys();
|
||||
console.log('Available top-level keys:', availableKeys);
|
||||
|
||||
// Validate if a key exists
|
||||
if (helpers.isValidKey('info')) {
|
||||
console.log('info is a valid top-level key');
|
||||
}
|
||||
|
||||
return {
|
||||
'x-example-enhanced': before('info'),
|
||||
'x-example-config': after('paths'),
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
25
src/extensions/vendor/example-usage.ts
vendored
25
src/extensions/vendor/example-usage.ts
vendored
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Example Vendor Extensions
|
||||
*/
|
||||
|
||||
// Function-based extensions with before/after helpers
|
||||
export const extensions = {
|
||||
'top-level': (before: (key: string) => number, after: (key: string) => number) => {
|
||||
return {
|
||||
'x-example-before-info': before('info'), // Before 'info'
|
||||
'x-example-after-paths': after('paths'), // After 'paths'
|
||||
};
|
||||
},
|
||||
'operation': (before: (key: string) => number, after: (key: string) => number) => {
|
||||
return {
|
||||
'x-example-before-parameters': before('parameters'), // Before 'parameters'
|
||||
'x-example-after-responses': after('responses'), // After 'responses'
|
||||
};
|
||||
},
|
||||
'schema': (before: (key: string) => number, after: (key: string) => number) => {
|
||||
return {
|
||||
'x-example-validation': after('type'), // After 'type'
|
||||
'x-example-example': after('example'), // After 'example'
|
||||
};
|
||||
}
|
||||
};
|
||||
37
src/extensions/vendor/postman.ts
vendored
37
src/extensions/vendor/postman.ts
vendored
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Postman Extensions
|
||||
*
|
||||
* Postman collection extensions for OpenAPI formatting.
|
||||
* Website: https://postman.com
|
||||
*/
|
||||
|
||||
import { defineConfig } from "../index.js";
|
||||
|
||||
// Function-based extensions with before/after helpers
|
||||
export const postman = defineConfig({
|
||||
info: {
|
||||
name: 'Postman',
|
||||
website: 'https://postman.com',
|
||||
support: 'support@postman.com'
|
||||
},
|
||||
extensions: {
|
||||
'top-level': (before, after) => {
|
||||
return {
|
||||
'x-postman-collection': before('info'), // Before 'info'
|
||||
'x-postman-version': after('paths'), // After 'paths'
|
||||
};
|
||||
},
|
||||
'operation': (before, after) => {
|
||||
return {
|
||||
'x-postman-test': after('responses'), // After 'responses'
|
||||
'x-postman-pre-request': before('parameters'), // Before 'parameters'
|
||||
};
|
||||
},
|
||||
'schema': (before, after) => {
|
||||
return {
|
||||
'x-postman-example': after('example'), // After 'example'
|
||||
'x-postman-mock': after('deprecated'), // After 'deprecated'
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
42
src/extensions/vendor/redoc.ts
vendored
42
src/extensions/vendor/redoc.ts
vendored
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Redoc Extensions
|
||||
*
|
||||
* Redoc documentation extensions for OpenAPI formatting.
|
||||
* Website: https://redocly.com
|
||||
*/
|
||||
|
||||
import { defineConfig } from "../index.js";
|
||||
|
||||
// Function-based extensions with before/after helpers
|
||||
export const redoc = defineConfig({
|
||||
info: {
|
||||
name: 'Redocly',
|
||||
website: 'https://redocly.com',
|
||||
support: 'team@redocly.com'
|
||||
},
|
||||
extensions: {
|
||||
'top-level': (before, after) => {
|
||||
return {
|
||||
'x-redoc-version': before('info'), // Before 'info'
|
||||
'x-redoc-theme': after('paths'), // After 'paths'
|
||||
};
|
||||
},
|
||||
'info': (before, after) => {
|
||||
return {
|
||||
'x-redoc-info': after('version'), // After 'version'
|
||||
};
|
||||
},
|
||||
'operation': (before, after) => {
|
||||
return {
|
||||
'x-redoc-group': after('tags'), // After 'tags'
|
||||
'x-redoc-hide': before('responses'), // Before 'responses'
|
||||
};
|
||||
},
|
||||
'schema': (before, after) => {
|
||||
return {
|
||||
'x-redoc-example': after('example'), // After 'example'
|
||||
'x-redoc-readonly': after('deprecated'), // After 'deprecated'
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
65
src/extensions/vendor/speakeasy.ts
vendored
65
src/extensions/vendor/speakeasy.ts
vendored
@@ -17,72 +17,15 @@ export const speakeasy = defineConfig({
|
||||
extensions: {
|
||||
'top-level': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-sdk': before('info'), // Before 'info'
|
||||
'x-speakeasy-auth': after('paths'), // After 'paths'
|
||||
};
|
||||
},
|
||||
'info': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-info': after('version'), // After 'version'
|
||||
'x-speakeasy-globals': after('security'),
|
||||
'x-speakeasy-globals-hidden': after('security'),
|
||||
};
|
||||
},
|
||||
'operation': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-retries': after('parameters'), // After 'parameters'
|
||||
'x-speakeasy-timeout': before('responses'), // Before 'responses'
|
||||
'x-speakeasy-cache': after('servers'), // After 'servers'
|
||||
'x-speakeasy-pagination': after('parameters'),
|
||||
'x-speakeasy-usage-example': after('deprecated'),
|
||||
};
|
||||
},
|
||||
'schema': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-validation': after('type'), // After 'type'
|
||||
'x-speakeasy-example': after('example'), // After 'example'
|
||||
};
|
||||
},
|
||||
'parameter': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-param': after('schema'), // After 'schema'
|
||||
};
|
||||
},
|
||||
'response': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-response': after('description'), // After 'description'
|
||||
};
|
||||
},
|
||||
'securityScheme': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-auth': after('type'), // After 'type'
|
||||
};
|
||||
},
|
||||
'server': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-server': after('url'), // After 'url'
|
||||
};
|
||||
},
|
||||
'tag': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-tag': after('name'), // After 'name'
|
||||
};
|
||||
},
|
||||
'externalDocs': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-docs': after('url'), // After 'url'
|
||||
};
|
||||
},
|
||||
'webhook': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-webhook': after('operationId'), // After 'operationId'
|
||||
};
|
||||
},
|
||||
'definitions': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-definition': after('type'), // After 'type'
|
||||
};
|
||||
},
|
||||
'securityDefinitions': (before, after) => {
|
||||
return {
|
||||
'x-speakeasy-security': after('type'), // After 'type'
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -11,8 +11,7 @@ describe('Vendor Extension System', () => {
|
||||
|
||||
// Check if extensions were loaded
|
||||
expect(vendorExtensions['top-level']).toBeDefined();
|
||||
expect(vendorExtensions['top-level']['x-speakeasy-sdk']).toBe(3); // before('info') = position 3
|
||||
expect(vendorExtensions['top-level']['x-speakeasy-auth']).toBe(14); // after('paths') = position 14
|
||||
expect(vendorExtensions['top-level']['x-tagGroups']).toBe(13);
|
||||
});
|
||||
|
||||
|
||||
@@ -28,12 +27,6 @@ describe('Vendor Extension System', () => {
|
||||
|
||||
// Check that extensions have the right structure
|
||||
expect(vendorExtensions['top-level']).toBeDefined();
|
||||
expect(vendorExtensions['info']).toBeDefined();
|
||||
expect(vendorExtensions['operation']).toBeDefined();
|
||||
expect(vendorExtensions['schema']).toBeDefined();
|
||||
|
||||
// Check specific extensions
|
||||
expect(vendorExtensions['top-level']['x-speakeasy-sdk']).toBe(3); // before('info') = position 3
|
||||
expect(vendorExtensions['top-level']['x-speakeasy-auth']).toBe(14); // after('paths') = position 14
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user