Skip to content

Commit

Permalink
feat: parse & stringify comments (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip authored Mar 4, 2024
1 parent 8b5a10c commit c7f1c66
Show file tree
Hide file tree
Showing 10 changed files with 611 additions and 53 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
"test.watch": "yarn test --watch"
},
"dependencies": {
"@stoplight/ordered-object-literal": "^1.0.1",
"@stoplight/types": "^13.0.0",
"@stoplight/yaml-ast-parser": "0.0.48",
"@stoplight/ordered-object-literal": "^1.0.5",
"@stoplight/types": "^14.1.1",
"@stoplight/yaml-ast-parser": "0.0.50",
"tslib": "^2.2.0"
},
"devDependencies": {
Expand Down
23 changes: 23 additions & 0 deletions src/__tests__/__snapshots__/parseWithPointers.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,28 @@ Array [
exports[`yaml parser parse diverse 1`] = `
Object {
"ast": Object {
"comments": Array [
Object {
"endPosition": 46,
"startPosition": 4,
"value": " <- yaml supports comments, json does not",
},
Object {
"endPosition": 89,
"startPosition": 47,
"value": " did you know you can embed json in yaml?",
},
Object {
"endPosition": 122,
"startPosition": 90,
"value": " try uncommenting the next line",
},
Object {
"endPosition": 139,
"startPosition": 123,
"value": " { foo: 'bar' }",
},
],
"endPosition": 442,
"errors": Array [],
"kind": 2,
Expand Down Expand Up @@ -480,6 +502,7 @@ to save space",
"parent": null,
"startPosition": 141,
},
"comments": Object {},
"data": Object {
"content": "Or we
can auto
Expand Down
67 changes: 67 additions & 0 deletions src/__tests__/fixtures/openapi-with-comments.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# my openapi document
openapi: 3.0.0 # Specifies the OpenAPI Specification version
info: # Metadata about the API
title: Bookstore Inventory API # The name of your API
description: API for managing a bookstore's inventory.
version: 1.0.0 # API version
servers: # Defines the API server and base URL
- url: 'https://api.bookstore.com/v1' # Base URL for the API endpoints
# Paths section describes the endpoints available in the API
paths:
/books:
get: # Retrieves a list of books from the inventory
summary: List all books
# A more detailed description of the operation
description: Retrieve a list of books available in the bookstore inventory.
responses: # Describes the possible responses
'200': # HTTP status code for a successful response
description: A JSON array of book objects.
content:
application/json: # Media type
schema:
type: array
items:
$ref: '#/components/schemas/Book' # References the Book schema
# Endpoint to create a new book entry
post:
summary: Add a new book
description: Add a new book to the bookstore inventory.
requestBody: # Describes the request body
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewBook' # Schema for the new book data
responses:
'201': # Status code for a successful creation
description: Book created successfully.
# Components section for reusable schemas
components:
schemas:
# Schema definition for a book
Book:
type: object
properties:
id:
type: string # Unique identifier for the book
description: The book's unique identifier.
title:
type: string # The title of the book
description: The title of the book.
author:
type: string # The author's name
description: The author of the book.
isbn:
type: string # The ISBN number
description: The International Standard Book Number.
# Indicates if the book is currently available
available:
type: boolean
description: Whether the book is currently available for sale.
# Schema for creating a new book entry
NewBook: # Below the schema definition
type: object
properties:
title:
type: string
description: The title of the book. # Inline comment for
174 changes: 174 additions & 0 deletions src/__tests__/parseWithPointers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,180 @@ describe('yaml parser', () => {
expect(result.data).toEqual(HugeJSON);
});

test('given attachComments set to true, parse with comments', () => {
const document = fs.readFileSync(path.join(__dirname, './fixtures/openapi-with-comments.yaml'), 'utf8');
const result = parseWithPointers(document, { attachComments: true });

expect(result.comments).toStrictEqual({
'#': [
{
placement: 'leading',
value: ' my openapi document',
},
{
value: ' Paths section describes the endpoints available in the API',
placement: 'between',
between: ['servers', 'paths'],
},
{
value: ' Components section for reusable schemas',
placement: 'between',
between: ['paths', 'components'],
},
],
'#/components/schemas': [
{
value: ' Schema for creating a new book entry',
placement: 'between',
between: ['Book', 'NewBook'],
},
{
value: ' Schema definition for a book',
placement: 'leading',
},
],
'#/components/schemas/Book/properties': [
{
value: ' Indicates if the book is currently available',
placement: 'between',
between: ['isbn', 'available'],
},
],
'#/components/schemas/Book/properties/author/type': [
{
value: " The author's name",
placement: 'before-eol',
},
],
'#/components/schemas/Book/properties/id/type': [
{
value: ' Unique identifier for the book',
placement: 'before-eol',
},
],
'#/components/schemas/Book/properties/isbn/type': [
{
value: ' The ISBN number',
placement: 'before-eol',
},
],
'#/components/schemas/Book/properties/title/type': [
{
value: ' The title of the book',
placement: 'before-eol',
},
],
'#/components/schemas/NewBook': [
{
value: ' Below the schema definition',
placement: 'before-eol',
},
],
'#/components/schemas/NewBook/properties/title/description': [
{
value: ' Inline comment for',
placement: 'before-eol',
},
],
'#/info': [
{
value: ' Metadata about the API',
placement: 'before-eol',
},
],
'#/info/title': [
{
value: ' The name of your API',
placement: 'before-eol',
},
],
'#/info/version': [
{
value: ' API version',
placement: 'before-eol',
},
],
'#/openapi': [
{
value: ' Specifies the OpenAPI Specification version',
placement: 'before-eol',
},
],
'#/paths/~1books': [
{
value: ' Endpoint to create a new book entry',
placement: 'between',
between: ['get', 'post'],
},
],
'#/paths/~1books/get': [
{
value: ' A more detailed description of the operation',
placement: 'between',
between: ['summary', 'description'],
},
{
value: ' Retrieves a list of books from the inventory',
placement: 'before-eol',
},
],
'#/paths/~1books/get/responses': [
{
value: ' Describes the possible responses',
placement: 'before-eol',
},
],
'#/paths/~1books/get/responses/200': [
{
value: ' HTTP status code for a successful response',
placement: 'before-eol',
},
],
'#/paths/~1books/get/responses/200/content/application~1json': [
{
value: ' Media type',
placement: 'before-eol',
},
],
'#/paths/~1books/get/responses/200/content/application~1json/schema/items/$ref': [
{
value: ' References the Book schema',
placement: 'before-eol',
},
],
'#/paths/~1books/post/requestBody': [
{
value: ' Describes the request body',
placement: 'before-eol',
},
],
'#/paths/~1books/post/requestBody/content/application~1json/schema/$ref': [
{
value: ' Schema for the new book data',
placement: 'before-eol',
},
],
'#/paths/~1books/post/responses/201': [
{
value: ' Status code for a successful creation',
placement: 'before-eol',
},
],
'#/servers': [
{
value: ' Defines the API server and base URL',
placement: 'before-eol',
},
],
'#/servers/0/url': [
{
value: ' Base URL for the API endpoints',
placement: 'before-eol',
},
],
});
});

test('parses string according to YAML 1.2 spec', () => {
const { data } = parseWithPointers(spectral481);
expect(data).toHaveProperty(
Expand Down
10 changes: 10 additions & 0 deletions src/__tests__/safeStringify.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import * as fs from 'fs';
import * as path from 'path';
import { parseWithPointers } from '../parseWithPointers';
import { safeStringify } from '../safeStringify';

describe('safeStringify', () => {
Expand All @@ -16,6 +19,13 @@ describe('safeStringify', () => {
expect(safeStringify(value)).toEqual(`${value}\n`);
});

it('should dump comments back', () => {
const document = fs.readFileSync(path.join(__dirname, './fixtures/openapi-with-comments.yaml'), 'utf8');
const result = parseWithPointers(document, { attachComments: true });

expect(safeStringify(result.data, { comments: result.comments })).toEqual(document);
});

it('should respect lineWidth for multi-line strings', () => {
const description = `# API information
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ export * from './dereferenceAnchor';
export * from './getJsonPathForPosition';
export * from './getLocationForJsonPath';
export * from './lineForPosition';
export * from './parse';
export * from './parseWithPointers';
export { parse } from './parse';
export { parseWithPointers } from './parseWithPointers';
export * from './safeStringify';
export * from './types';
export * from './trapAccess';
6 changes: 2 additions & 4 deletions src/parse.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { load as loadAST } from '@stoplight/yaml-ast-parser';
import { walkAST } from './parseWithPointers';
import { YAMLNode } from './types';
import { parseWithPointers } from './parseWithPointers';

export const parse = <T>(value: string): T => walkAST(loadAST(value) as YAMLNode, void 0, [], []) as T;
export const parse = <T>(value: string): T => parseWithPointers(value).data as T;
Loading

0 comments on commit c7f1c66

Please sign in to comment.