diff --git a/.changeset/dirty-horses-nail.md b/.changeset/dirty-horses-nail.md new file mode 100644 index 000000000..92595ea59 --- /dev/null +++ b/.changeset/dirty-horses-nail.md @@ -0,0 +1,5 @@ +--- +'@openapi-qraft/plugin': minor +--- + +Replaced `micromatch` with `picomatch`. diff --git a/packages/openapi-plugin/package.json b/packages/openapi-plugin/package.json index 227ae0f3b..c27650ce0 100644 --- a/packages/openapi-plugin/package.json +++ b/packages/openapi-plugin/package.json @@ -38,15 +38,15 @@ "ansi-colors": "^4.1.3", "camelcase": "^8.0.0", "commander": "^14.0.3", - "micromatch": "^4.0.8", "openapi-typescript": "^7.13.0", - "ora": "^9.3.0" + "ora": "^9.3.0", + "picomatch": "^4.0.4" }, "devDependencies": { "@openapi-qraft/eslint-config": "workspace:*", "@openapi-qraft/test-fixtures": "workspace:*", - "@types/micromatch": "^4.0.10", "@types/node": "^22.19.17", + "@types/picomatch": "^4.0.3", "@vitest/coverage-v8": "^4.1.2", "eslint": "^10.2.0", "memfs": "^4.57.1", diff --git a/packages/openapi-plugin/src/lib/createServicePathMatch.ts b/packages/openapi-plugin/src/lib/createServicePathMatch.ts index bb1aabcf9..f11e5e274 100644 --- a/packages/openapi-plugin/src/lib/createServicePathMatch.ts +++ b/packages/openapi-plugin/src/lib/createServicePathMatch.ts @@ -1,4 +1,4 @@ -import micromatch from 'micromatch'; +import picomatch from 'picomatch'; /** * Create a function to match service paths @@ -20,7 +20,7 @@ export const createServicePathMatch = (servicesGlob: string[]) => { ); return function isServicePatchMatch(path: string) { - return micromatch.isMatch(path, servicePathGlobs.match, { + return picomatch.isMatch(path, servicePathGlobs.match, { ignore: servicePathGlobs.ignore, }); }; diff --git a/packages/openapi-plugin/src/lib/filterDocumentPaths.spec.ts b/packages/openapi-plugin/src/lib/filterDocumentPaths.spec.ts index fb7331f57..70ac64b32 100644 --- a/packages/openapi-plugin/src/lib/filterDocumentPaths.spec.ts +++ b/packages/openapi-plugin/src/lib/filterDocumentPaths.spec.ts @@ -29,6 +29,47 @@ describe('filterDocumentPaths', () => { const isPathMatch = createServicePathMatch([]); expect(isPathMatch('/user/1')).toBe(false); }); + + it('should match OpenAPI-style path params in braces', () => { + const isPathMatch = createServicePathMatch(['/users/*']); + + expect(isPathMatch('/users/{id}')).toBe(true); + expect(isPathMatch('/users/{userId}')).toBe(true); + expect(isPathMatch('/users/{id}/posts')).toBe(false); + }); + + it('should support brace expansion patterns', () => { + const isPathMatch = createServicePathMatch(['/{users,posts}/**']); + + expect(isPathMatch('/users/1')).toBe(true); + expect(isPathMatch('/posts/1')).toBe(true); + expect(isPathMatch('/profiles/1')).toBe(false); + }); + + it('should apply ignore patterns regardless of input order', () => { + const firstIncludeThenIgnore = createServicePathMatch([ + '/admin/**', + '!/admin/internal/**', + ]); + const firstIgnoreThenInclude = createServicePathMatch([ + '!/admin/internal/**', + '/admin/**', + ]); + + const paths = [ + '/admin/users', + '/admin/internal', + '/admin/internal/audit', + ]; + + for (const path of paths) { + expect(firstIncludeThenIgnore(path)).toBe(firstIgnoreThenInclude(path)); + } + + expect(firstIncludeThenIgnore('/admin/users')).toBe(true); + expect(firstIncludeThenIgnore('/admin/internal')).toBe(false); + expect(firstIncludeThenIgnore('/admin/internal/audit')).toBe(false); + }); }); describe('filterDocumentPaths', () => { diff --git a/yarn.lock b/yarn.lock index 98a9e6de5..d02af1c1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4340,17 +4340,17 @@ __metadata: "@openapi-qraft/test-fixtures": "workspace:*" "@qraft/plugin": "workspace:*" "@redocly/openapi-core": "npm:^1.34.11" - "@types/micromatch": "npm:^4.0.10" "@types/node": "npm:^22.19.17" + "@types/picomatch": "npm:^4.0.3" "@vitest/coverage-v8": "npm:^4.1.2" ansi-colors: "npm:^4.1.3" camelcase: "npm:^8.0.0" commander: "npm:^14.0.3" eslint: "npm:^10.2.0" memfs: "npm:^4.57.1" - micromatch: "npm:^4.0.8" openapi-typescript: "npm:^7.13.0" ora: "npm:^9.3.0" + picomatch: "npm:^4.0.4" rimraf: "npm:^6.1.3" typescript: "npm:^5.9.3" vitest: "npm:^4.1.4" @@ -6286,13 +6286,6 @@ __metadata: languageName: node linkType: hard -"@types/braces@npm:*": - version: 3.0.4 - resolution: "@types/braces@npm:3.0.4" - checksum: 10c0/05220f330814364318a1c2f403046d717690cabf3adc8417357b0b12713f92768cf9df76e0e25212b5a2727c8c56765ff19a284c7ece39e0985d0d6fadb6c4aa - languageName: node - linkType: hard - "@types/chai@npm:^5.2.2": version: 5.2.3 resolution: "@types/chai@npm:5.2.3" @@ -6578,15 +6571,6 @@ __metadata: languageName: node linkType: hard -"@types/micromatch@npm:^4.0.10": - version: 4.0.10 - resolution: "@types/micromatch@npm:4.0.10" - dependencies: - "@types/braces": "npm:*" - checksum: 10c0/dc424e0f9ed1a4f22dbed5048ac698d089d4628dd8a41a056b2eb12782cfda85bf06fccc0be341ce9af7345858eb07a27757ca023664cfb8bde235212795d295 - languageName: node - linkType: hard - "@types/mime@npm:^1": version: 1.3.5 resolution: "@types/mime@npm:1.3.5" @@ -6633,6 +6617,13 @@ __metadata: languageName: node linkType: hard +"@types/picomatch@npm:^4.0.3": + version: 4.0.3 + resolution: "@types/picomatch@npm:4.0.3" + checksum: 10c0/974107ec98ea9a8b437f36bdf6ac9c57772fd66d9d746e43f6da2a7b97f6e03bc48226e5cdf2beb0b73b7a843cf486e7336ae15ab9017e47287dcb49d78499a2 + languageName: node + linkType: hard + "@types/pluralize@npm:^0.0.30": version: 0.0.30 resolution: "@types/pluralize@npm:0.0.30"