Skip to content

Commit b47e8e9

Browse files
authored
Allow non-JSON content types (#4)
* allow other content types like text/html * setup semantic release
1 parent 3a78f10 commit b47e8e9

9 files changed

Lines changed: 8970 additions & 3400 deletions

File tree

.github/workflows/release.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
concurrency:
9+
group: release-${{ github.ref }}
10+
cancel-in-progress: false
11+
12+
jobs:
13+
release:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: write
17+
issues: write
18+
pull-requests: write
19+
id-token: write
20+
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v6
24+
with:
25+
fetch-depth: 0
26+
27+
- name: Setup Node.js
28+
uses: actions/setup-node@v6
29+
with:
30+
node-version: 24
31+
cache: npm
32+
registry-url: https://registry.npmjs.org
33+
34+
- name: Install dependencies
35+
run: npm ci
36+
37+
- name: Build
38+
run: npm run build
39+
40+
- name: Test
41+
run: npm test
42+
43+
- name: Release
44+
run: npm run release
45+
env:
46+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

.releaserc.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"branches": ["main"],
3+
"tagFormat": "v${version}",
4+
"plugins": [
5+
[
6+
"@semantic-release/commit-analyzer",
7+
{
8+
"preset": "conventionalcommits"
9+
}
10+
],
11+
[
12+
"@semantic-release/release-notes-generator",
13+
{
14+
"preset": "conventionalcommits"
15+
}
16+
],
17+
[
18+
"@semantic-release/changelog",
19+
{
20+
"changelogFile": "CHANGELOG.md"
21+
}
22+
],
23+
[
24+
"@semantic-release/npm",
25+
{
26+
"npmPublish": true
27+
}
28+
],
29+
"@semantic-release/github",
30+
[
31+
"@semantic-release/git",
32+
{
33+
"assets": ["CHANGELOG.md", "package.json", "package-lock.json"],
34+
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
35+
}
36+
]
37+
]
38+
}

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [2.0.1] - 2026-04-07
9+
10+
### Changed
11+
12+
- The `content-type` rule now allows other non-JSON content, but still demands all JSON requests/responses use `application/vnd.api+json`.
13+
14+
## [2.0.1] - 2026-03-15
915

1016
### Added
1117
- Created TypeScript version of the ruleset. YAML still available.

CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,17 @@ It is intended to include rules that rely only on Spectral core functions. At th
6161
- Prefer small, reviewable commits.
6262
- Include a short summary of what changed and why.
6363
- Mention any rule behavior changes that may affect downstream users.
64+
65+
## Automated Releases
66+
67+
Releases are automated with semantic-release via GitHub Actions on pushes to `main`.
68+
69+
- Commits should follow Conventional Commits (`feat:`, `fix:`, `feat!:`) so the next version can be calculated correctly.
70+
- The workflow publishes to npm and creates a GitHub release and tag like `v2.1.0`.
71+
- Configure repository secret `NPM_TOKEN` with publish access to `@apisyouwonthate/spectral-jsonapi`.
72+
73+
You can test release logic locally in dry-run mode:
74+
75+
```bash
76+
npm run release -- --dry-run --no-ci
77+
```

README.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ Create a local ruleset file:
1515
```yaml
1616
# .spectral.yaml
1717
extends:
18-
- spectral:oas
19-
- "@apisyouwonthate/spectral-jsonapi"
18+
- spectral:oas
19+
- "@apisyouwonthate/spectral-jsonapi"
2020
```
2121
2222
Lint your OpenAPI description:
@@ -34,8 +34,8 @@ Use this when the project already installs dependencies with npm.
3434
```yaml
3535
# .spectral.yaml
3636
extends:
37-
- spectral:oas
38-
- "@apisyouwonthate/spectral-jsonapi"
37+
- spectral:oas
38+
- "@apisyouwonthate/spectral-jsonapi"
3939
```
4040
4141
### Use legacy YAML directly from GitHub
@@ -44,7 +44,7 @@ Use this when you want to consume the generated YAML ruleset without installing
4444
4545
```yaml
4646
extends:
47-
- "https://raw.githubusercontent.com/apisyouwonthate/spectral-jsonapi/refs/heads/main/.spectral.yml"
47+
- "https://raw.githubusercontent.com/apisyouwonthate/spectral-jsonapi/refs/heads/main/.spectral.yml"
4848
```
4949
5050
Once you have the ruleset set up, you can run Spectral in the same directory as your `.spectral.yml` ruleset, and it will include the JSON:API rules in its check
@@ -71,20 +71,20 @@ Set `x-jsonapi-virtual-resource: true` on the resource schema to skip the `resou
7171

7272
```yaml
7373
components:
74-
schemas:
75-
AvailableSlotResource:
76-
type: object
77-
x-jsonapi-virtual-resource: true
78-
required:
79-
- type
80-
- attributes
81-
properties:
82-
type:
83-
type: string
84-
enum:
85-
- availableSlot
86-
attributes:
87-
$ref: "#/components/schemas/AvailableSlotAttributes"
74+
schemas:
75+
AvailableSlotResource:
76+
type: object
77+
x-jsonapi-virtual-resource: true
78+
required:
79+
- type
80+
- attributes
81+
properties:
82+
type:
83+
type: string
84+
enum:
85+
- availableSlot
86+
attributes:
87+
$ref: "#/components/schemas/AvailableSlotAttributes"
8888
```
8989

9090
## 👥 Contributing

__tests__/content-type.test.ts

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,34 @@ describe("Rule content-type", () => {
6464
);
6565
});
6666

67+
it("allows non-json content type", async () => {
68+
await expectRuleErrors(
69+
spectral,
70+
"content-type",
71+
{
72+
openapi: "3.1.0",
73+
info: { title: "Test", version: "1.0.0" },
74+
paths: {
75+
"/items": {
76+
get: {
77+
responses: {
78+
"200": {
79+
description: "ok",
80+
content: {
81+
"text/html": {
82+
schema: { type: "string" },
83+
},
84+
},
85+
},
86+
},
87+
},
88+
},
89+
},
90+
},
91+
[],
92+
);
93+
});
94+
6795
it("invalid content type", async () => {
6896
await expectRuleErrors(
6997
spectral,
@@ -91,7 +119,7 @@ describe("Rule content-type", () => {
91119
[
92120
{
93121
message:
94-
"Use application/vnd.api+json for all request and response bodies.",
122+
"Use application/vnd.api+json for JSON request and response bodies.",
95123
path: [
96124
"paths",
97125
"/items",
@@ -106,4 +134,47 @@ describe("Rule content-type", () => {
106134
],
107135
);
108136
});
137+
138+
it("invalid +json content type", async () => {
139+
await expectRuleErrors(
140+
spectral,
141+
"content-type",
142+
{
143+
openapi: "3.1.0",
144+
info: { title: "Test", version: "1.0.0" },
145+
paths: {
146+
"/items": {
147+
get: {
148+
responses: {
149+
"200": {
150+
description: "ok",
151+
content: {
152+
"application/problem+json": {
153+
schema: { type: "object" },
154+
},
155+
},
156+
},
157+
},
158+
},
159+
},
160+
},
161+
},
162+
[
163+
{
164+
message:
165+
"Use application/vnd.api+json for JSON request and response bodies.",
166+
path: [
167+
"paths",
168+
"/items",
169+
"get",
170+
"responses",
171+
"200",
172+
"content",
173+
"application/problem+json",
174+
],
175+
severity: DiagnosticSeverity.Error,
176+
},
177+
],
178+
);
179+
});
109180
});

0 commit comments

Comments
 (0)