Skip to content

Commit

Permalink
feat(parser): rewriting the library in typescript (#932)
Browse files Browse the repository at this point in the history
## 🐳 Context

Now that our fork of
[@apidevtools/swagger-parser](https://npm.im/@apidevtools/swagger-parser)
has fully deviated and was moved here from [our other
repository](https://github.com/readmeio/openapi-parser) I have built off
of JonLuca's[^1] starting work over in
APIDevTools/swagger-parser#253 and rewritten the
library in TS.

This is all going to be the basis for a slew of forthcoming changes in
order to support error levels.

Because this is a full rewrite, and a lot of things have changed, this
will incur a major version bump.

## 🧰 Changes

* [x] Rewrote our OpenAPI parser in TS.
* [x] Added in dual support for CJS and ESM.
* [x] Resolved a number of test quirks and invalid false positive
assertions.

[^1]: Because I adapted heavily from his draft PR of this rewrite over
in the swagger-parser repository I've credited him as a co-author of
this work.

---------

Co-authored-by: JonLuca De Caro <[email protected]>
Co-authored-by: Kanad Gupta <[email protected]>
  • Loading branch information
3 people authored Feb 13, 2025
1 parent f24ed0f commit dec0e4b
Show file tree
Hide file tree
Showing 82 changed files with 3,083 additions and 3,184 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"extends": ["@readme/eslint-config", "@readme/eslint-config/typescript", "@readme/eslint-config/esm"],
"root": true
"root": true,
}
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages/parser/test/specs/large-file-memory-leak/cloudflare-stringified.json
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"editor.formatOnSave": true
}
2,804 changes: 1,661 additions & 1,143 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"./packages/*"
],
"devDependencies": {
"@readme/eslint-config": "^14.0.0",
"@readme/eslint-config": "^14.2.0",
"@vitest/coverage-v8": "^3.0.4",
"alex": "^11.0.1",
"eslint": "^8.57.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/oas-normalize/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
"eslint": "^8.57.0",
"nock": "^14.0.0",
"tsup": "^8.0.2",
"typescript": "^5.1.6"
"typescript": "^5.1.6",
"vitest": "^3.0.5"
},
"prettier": "@readme/eslint-config/prettier"
}
16 changes: 7 additions & 9 deletions packages/oas-normalize/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { Options } from './lib/types.js';
import type { ParserOptions } from '@readme/openapi-parser';
import type { OpenAPI, OpenAPIV2, OpenAPIV3 } from 'openapi-types';

import fs from 'node:fs';

import openapiParser from '@readme/openapi-parser';
import { OpenAPIParser } from '@readme/openapi-parser';
import postmanToOpenAPI from '@readme/postman-to-openapi';
import converter from 'swagger2openapi';

Expand Down Expand Up @@ -108,9 +109,8 @@ export default class OASNormalize {

return schema;
})
.then(schema => openapiParser.bundle(schema))
.then(schema => new OpenAPIParser().bundle(schema))
.then(bundle => {
// @ts-expect-error The typings on the parser are messed up rigth now while a rewrite is in progress.
this.cache.bundle = bundle;
return bundle;
});
Expand All @@ -134,9 +134,8 @@ export default class OASNormalize {

return schema;
})
.then(schema => openapiParser.dereference(schema))
.then(schema => new OpenAPIParser().dereference(schema))
.then(dereferenced => {
// @ts-expect-error The typings on the parser are messed up rigth now while a rewrite is in progress.
this.cache.deref = dereferenced;
return dereferenced;
});
Expand Down Expand Up @@ -181,7 +180,7 @@ export default class OASNormalize {
*/
async validate(
opts: {
parser?: openapiParser.Options;
parser?: ParserOptions;
} = {},
): Promise<true> {
const parserOptions = opts.parser || {};
Expand All @@ -208,7 +207,7 @@ export default class OASNormalize {
}

/**
* `openapiParser.validate()` dereferences schemas at the same time as validation, mutating
* `OpenAPIParser.validate()` dereferences schemas at the same time as validation, mutating
* the supplied parameter in the process, and does not give us an option to disable this.
* As we already have a dereferencing method on this library, and this method just needs to
* tell us if the API definition is valid or not, we need to clone the schema before
Expand All @@ -217,8 +216,7 @@ export default class OASNormalize {
// eslint-disable-next-line try-catch-failsafe/json-parse
const clonedSchema = JSON.parse(JSON.stringify(schema));

// @ts-expect-error The typings on the parser are messed up rigth now while a rewrite is in progress.
return openapiParser.validate(clonedSchema, parserOptions).then(() => {
return new OpenAPIParser().validate(clonedSchema, parserOptions).then(() => {
// The API definition, whatever its format or specification, is valid.
return true;
});
Expand Down
3 changes: 2 additions & 1 deletion packages/oas-to-har/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
"jest-expect-har": "^7.1.0",
"tsup": "^8.0.2",
"type-fest": "^4.18.3",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"vitest": "^3.0.5"
},
"prettier": "@readme/eslint-config/prettier"
}
3 changes: 2 additions & 1 deletion packages/oas-to-snippet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
"oas": "file:../oas",
"tsup": "^8.0.2",
"type-fest": "^4.18.3",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"vitest": "^3.0.5"
},
"prettier": "@readme/eslint-config/prettier"
}
3 changes: 2 additions & 1 deletion packages/oas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@
"@types/memoizee": "^0.4.11",
"@types/node": "^22.7.6",
"tsup": "^8.0.2",
"typescript": "^5.4.4"
"typescript": "^5.4.4",
"vitest": "^3.0.5"
},
"prettier": "@readme/eslint-config/prettier"
}
4 changes: 2 additions & 2 deletions packages/oas/test/operation/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type * as RMOAS from '../../src/types.js';

import petstoreSpec from '@readme/oas-examples/3.0/json/petstore.json';
import openapiParser from '@readme/openapi-parser';
import { OpenAPIParser } from '@readme/openapi-parser';
import { beforeAll, describe, it, expect } from 'vitest';

import Oas from '../../src/index.js';
Expand Down Expand Up @@ -708,7 +708,7 @@ describe('#getSecurityWithTypes()', () => {

// The original API doc should still be valid.
const clonedSpec = JSON.parse(JSON.stringify(spec.api));
await expect(openapiParser.validate(clonedSpec)).resolves.toStrictEqual(
await expect(OpenAPIParser.validate(clonedSpec)).resolves.toStrictEqual(
expect.objectContaining({
openapi: '3.1.0',
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { HttpMethods, ResponseObject, SchemaObject } from '../../../src/types.js';

import openapiParser from '@readme/openapi-parser';
import { OpenAPIParser } from '@readme/openapi-parser';
import { beforeAll, describe, test, expect, it } from 'vitest';

import Oas from '../../../src/index.js';
Expand Down Expand Up @@ -301,7 +301,7 @@ describe('quirks', () => {
});

// The original spec should still validate too!
await expect(openapiParser.validate(cloneObject(definition))).resolves.toStrictEqual(
await expect(OpenAPIParser.validate(cloneObject(definition))).resolves.toStrictEqual(
expect.objectContaining({
openapi: '3.0.2',
}),
Expand Down
27 changes: 0 additions & 27 deletions packages/parser/.editorconfig

This file was deleted.

47 changes: 4 additions & 43 deletions packages/parser/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,49 +1,10 @@
{
"root": true,
"extends": [
"@readme/eslint-config",
"@readme/eslint-config/typescript",
],
"env": {
"browser": true,
"node": true
},
"rules": {
"camelcase": ["error", { "allow": ["OpenAPIV3_1"] }],
"lines-between-class-members": "off",
"no-continue": "off",
"no-plusplus": "off",
"no-restricted-syntax": "off",
"no-underscore-dangle": "off",
"no-use-before-define": "off",
"prefer-rest-params": "off",
"prefer-spread": "off"
"prefer-spread": "off",
},
"overrides": [
{
// The typings in this file are pretty bad right now, when we have native types we can
// remove this.
"files": ["lib/index.d.ts"],
"rules": {
"@typescript-eslint/consistent-indexed-object-style": "off",
"@typescript-eslint/consistent-type-imports": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/sort-type-constituents": "off",
"eslint-comments/no-unused-disable": "off",
"lines-between-class-members": "off",
"max-classes-per-file": "off",
"quotes": "off",
"typescript-sort-keys/interface": "off"
}
},
{
// These can all get removed when the library is moved over to native TS.
"files": ["*.js"],
"rules": {
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-var-requires": "off",
"eslint-comments/no-unused-disable": "off",
"func-names": "off"
}
}
]
}
26 changes: 0 additions & 26 deletions packages/parser/.gitattributes

This file was deleted.

32 changes: 0 additions & 32 deletions packages/parser/.github/dependabot.yml

This file was deleted.

39 changes: 0 additions & 39 deletions packages/parser/.github/workflows/ci.yml

This file was deleted.

19 changes: 0 additions & 19 deletions packages/parser/.github/workflows/lint-pr-title.yml

This file was deleted.

Loading

0 comments on commit dec0e4b

Please sign in to comment.