sourcey 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +254 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +197 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/copy.js +21 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/scroll-tracker.js +88 -0
- package/dist/client/search.js +164 -0
- package/dist/client/sidebar.js +54 -0
- package/dist/client/tabs.js +51 -0
- package/dist/client/theme-toggle.js +36 -0
- package/dist/components/App.d.ts +15 -0
- package/dist/components/App.d.ts.map +1 -0
- package/dist/components/App.js +13 -0
- package/dist/components/App.js.map +1 -0
- package/dist/components/layout/Head.d.ts +2 -0
- package/dist/components/layout/Head.d.ts.map +1 -0
- package/dist/components/layout/Head.js +29 -0
- package/dist/components/layout/Head.js.map +1 -0
- package/dist/components/layout/Header.d.ts +13 -0
- package/dist/components/layout/Header.d.ts.map +1 -0
- package/dist/components/layout/Header.js +38 -0
- package/dist/components/layout/Header.js.map +1 -0
- package/dist/components/layout/Page.d.ts +2 -0
- package/dist/components/layout/Page.d.ts.map +1 -0
- package/dist/components/layout/Page.js +29 -0
- package/dist/components/layout/Page.js.map +1 -0
- package/dist/components/layout/Sidebar.d.ts +9 -0
- package/dist/components/layout/Sidebar.d.ts.map +1 -0
- package/dist/components/layout/Sidebar.js +41 -0
- package/dist/components/layout/Sidebar.js.map +1 -0
- package/dist/components/layout/TableOfContents.d.ts +10 -0
- package/dist/components/layout/TableOfContents.d.ts.map +1 -0
- package/dist/components/layout/TableOfContents.js +12 -0
- package/dist/components/layout/TableOfContents.js.map +1 -0
- package/dist/components/openapi/CodeSamples.d.ts +11 -0
- package/dist/components/openapi/CodeSamples.d.ts.map +1 -0
- package/dist/components/openapi/CodeSamples.js +16 -0
- package/dist/components/openapi/CodeSamples.js.map +1 -0
- package/dist/components/openapi/Definition.d.ts +11 -0
- package/dist/components/openapi/Definition.d.ts.map +1 -0
- package/dist/components/openapi/Definition.js +12 -0
- package/dist/components/openapi/Definition.js.map +1 -0
- package/dist/components/openapi/EndpointBar.d.ts +12 -0
- package/dist/components/openapi/EndpointBar.d.ts.map +1 -0
- package/dist/components/openapi/EndpointBar.js +22 -0
- package/dist/components/openapi/EndpointBar.js.map +1 -0
- package/dist/components/openapi/Introduction.d.ts +6 -0
- package/dist/components/openapi/Introduction.d.ts.map +1 -0
- package/dist/components/openapi/Introduction.js +14 -0
- package/dist/components/openapi/Introduction.js.map +1 -0
- package/dist/components/openapi/Operation.d.ts +13 -0
- package/dist/components/openapi/Operation.d.ts.map +1 -0
- package/dist/components/openapi/Operation.js +23 -0
- package/dist/components/openapi/Operation.js.map +1 -0
- package/dist/components/openapi/Parameters.d.ts +7 -0
- package/dist/components/openapi/Parameters.d.ts.map +1 -0
- package/dist/components/openapi/Parameters.js +10 -0
- package/dist/components/openapi/Parameters.js.map +1 -0
- package/dist/components/openapi/RequestBody.d.ts +17 -0
- package/dist/components/openapi/RequestBody.d.ts.map +1 -0
- package/dist/components/openapi/RequestBody.js +27 -0
- package/dist/components/openapi/RequestBody.js.map +1 -0
- package/dist/components/openapi/Responses.d.ts +14 -0
- package/dist/components/openapi/Responses.d.ts.map +1 -0
- package/dist/components/openapi/Responses.js +60 -0
- package/dist/components/openapi/Responses.js.map +1 -0
- package/dist/components/openapi/Security.d.ts +14 -0
- package/dist/components/openapi/Security.d.ts.map +1 -0
- package/dist/components/openapi/Security.js +32 -0
- package/dist/components/openapi/Security.js.map +1 -0
- package/dist/components/openapi/Tags.d.ts +8 -0
- package/dist/components/openapi/Tags.d.ts.map +1 -0
- package/dist/components/openapi/Tags.js +10 -0
- package/dist/components/openapi/Tags.js.map +1 -0
- package/dist/components/schema/ExampleView.d.ts +11 -0
- package/dist/components/schema/ExampleView.d.ts.map +1 -0
- package/dist/components/schema/ExampleView.js +15 -0
- package/dist/components/schema/ExampleView.js.map +1 -0
- package/dist/components/schema/SchemaDatatype.d.ts +11 -0
- package/dist/components/schema/SchemaDatatype.d.ts.map +1 -0
- package/dist/components/schema/SchemaDatatype.js +36 -0
- package/dist/components/schema/SchemaDatatype.js.map +1 -0
- package/dist/components/schema/SchemaView.d.ts +14 -0
- package/dist/components/schema/SchemaView.d.ts.map +1 -0
- package/dist/components/schema/SchemaView.js +44 -0
- package/dist/components/schema/SchemaView.js.map +1 -0
- package/dist/components/ui/Badge.d.ts +11 -0
- package/dist/components/ui/Badge.d.ts.map +1 -0
- package/dist/components/ui/Badge.js +14 -0
- package/dist/components/ui/Badge.js.map +1 -0
- package/dist/components/ui/Markdown.d.ts +8 -0
- package/dist/components/ui/Markdown.d.ts.map +1 -0
- package/dist/components/ui/Markdown.js +13 -0
- package/dist/components/ui/Markdown.js.map +1 -0
- package/dist/components/ui/SectionLabel.d.ts +10 -0
- package/dist/components/ui/SectionLabel.d.ts.map +1 -0
- package/dist/components/ui/SectionLabel.js +9 -0
- package/dist/components/ui/SectionLabel.js.map +1 -0
- package/dist/config.d.ts +46 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +102 -0
- package/dist/config.js.map +1 -0
- package/dist/core/converter.d.ts +9 -0
- package/dist/core/converter.d.ts.map +1 -0
- package/dist/core/converter.js +29 -0
- package/dist/core/converter.js.map +1 -0
- package/dist/core/loader.d.ts +7 -0
- package/dist/core/loader.d.ts.map +1 -0
- package/dist/core/loader.js +92 -0
- package/dist/core/loader.js.map +1 -0
- package/dist/core/markdown-loader.d.ts +29 -0
- package/dist/core/markdown-loader.d.ts.map +1 -0
- package/dist/core/markdown-loader.js +65 -0
- package/dist/core/markdown-loader.js.map +1 -0
- package/dist/core/navigation.d.ts +51 -0
- package/dist/core/navigation.d.ts.map +1 -0
- package/dist/core/navigation.js +108 -0
- package/dist/core/navigation.js.map +1 -0
- package/dist/core/normalizer.d.ts +7 -0
- package/dist/core/normalizer.d.ts.map +1 -0
- package/dist/core/normalizer.js +472 -0
- package/dist/core/normalizer.js.map +1 -0
- package/dist/core/parser.d.ts +10 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +34 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/search-indexer.d.ts +25 -0
- package/dist/core/search-indexer.d.ts.map +1 -0
- package/dist/core/search-indexer.js +72 -0
- package/dist/core/search-indexer.js.map +1 -0
- package/dist/core/types.d.ts +236 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +7 -0
- package/dist/core/types.js.map +1 -0
- package/dist/dev-server.d.ts +17 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/dev-server.js +202 -0
- package/dist/dev-server.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/dist/renderer/context.d.ts +46 -0
- package/dist/renderer/context.d.ts.map +1 -0
- package/dist/renderer/context.js +22 -0
- package/dist/renderer/context.js.map +1 -0
- package/dist/renderer/html-builder.d.ts +37 -0
- package/dist/renderer/html-builder.d.ts.map +1 -0
- package/dist/renderer/html-builder.js +87 -0
- package/dist/renderer/html-builder.js.map +1 -0
- package/dist/renderer/static-renderer.d.ts +10 -0
- package/dist/renderer/static-renderer.d.ts.map +1 -0
- package/dist/renderer/static-renderer.js +17 -0
- package/dist/renderer/static-renderer.js.map +1 -0
- package/dist/themes/default/main.css +61 -0
- package/dist/themes/default/sourcey.css +425 -0
- package/dist/utils/code-samples.d.ts +8 -0
- package/dist/utils/code-samples.d.ts.map +1 -0
- package/dist/utils/code-samples.js +84 -0
- package/dist/utils/code-samples.js.map +1 -0
- package/dist/utils/example-generator.d.ts +12 -0
- package/dist/utils/example-generator.d.ts.map +1 -0
- package/dist/utils/example-generator.js +123 -0
- package/dist/utils/example-generator.js.map +1 -0
- package/dist/utils/highlighter.d.ts +10 -0
- package/dist/utils/highlighter.d.ts.map +1 -0
- package/dist/utils/highlighter.js +51 -0
- package/dist/utils/highlighter.js.map +1 -0
- package/dist/utils/html-id.d.ts +6 -0
- package/dist/utils/html-id.d.ts.map +1 -0
- package/dist/utils/html-id.js +11 -0
- package/dist/utils/html-id.js.map +1 -0
- package/dist/utils/http.d.ts +14 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +97 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/markdown.d.ts +9 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/utils/markdown.js +18 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/vite-plugin.d.ts +16 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +92 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Copyright (c) 2015-2017 Kam Low
|
|
2
|
+
Code from bootprint-openapi and bootprint-json-schema Copyright (c) 2015 Nils Knappmeier
|
|
3
|
+
|
|
4
|
+
MIT License
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
a copy of this software and associated documentation files (the
|
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be
|
|
15
|
+
included in all copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Sourcey
|
|
2
|
+
|
|
3
|
+
> Beautiful API documentation from OpenAPI specs
|
|
4
|
+
|
|
5
|
+
[](https://github.com/sourcey/sourcey/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/sourcey)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](https://github.com/sourcey/sourcey/blob/master/LICENSE)
|
|
9
|
+
|
|
10
|
+
Sourcey generates beautiful static HTML documentation from [OpenAPI](https://openapis.org) / [Swagger](http://swagger.io) specifications.
|
|
11
|
+
|
|
12
|
+
Supports **OpenAPI 3.0**, **OpenAPI 3.1**, and **Swagger 2.0** (auto-converted).
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **OpenAPI 3.x + Swagger 2.0** — full support, with automatic Swagger-to-OpenAPI conversion
|
|
19
|
+
- **Beautiful layout** — sidebar navigation, docs, and code examples
|
|
20
|
+
- **Dark mode** — toggle with a button, respects system preference, persists with localStorage
|
|
21
|
+
- **Client-side search** — `/` or `Ctrl+K` to search endpoints and models instantly
|
|
22
|
+
- **Auto-generated code samples** — cURL, JavaScript (fetch), and Python (requests) for every operation
|
|
23
|
+
- **Synced language tabs** — switch language in one example, all others follow
|
|
24
|
+
- **Shiki syntax highlighting** — VS Code-quality highlighting at build time
|
|
25
|
+
- **Markdown descriptions** — full markdown rendering in all description fields
|
|
26
|
+
- **Dev server with live reload** — `sourcey dev` watches your spec and reloads the browser
|
|
27
|
+
- **Theming** — override any CSS variable via `sourcey.json` — colors, fonts, spacing
|
|
28
|
+
- **Custom branding** — logo and favicon via config
|
|
29
|
+
- **Embeddable output** — generate partial HTML for embedding into your own site
|
|
30
|
+
- **Zero client-side dependencies** — no frameworks shipped to the browser
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
Install Sourcey:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -g sourcey
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Generate docs from your spec:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
sourcey build your_api.yaml -o docs/
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
That's it. Open `docs/index.html` in your browser.
|
|
47
|
+
|
|
48
|
+
## CLI
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
sourcey build <spec> [options] Build static documentation
|
|
52
|
+
sourcey dev <spec> [options] Start dev server with live reload
|
|
53
|
+
sourcey validate <spec> Validate a spec file
|
|
54
|
+
sourcey <spec> Shorthand for 'sourcey build'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Build options
|
|
58
|
+
|
|
59
|
+
| Flag | Alias | Description | Default |
|
|
60
|
+
|------|-------|-------------|---------|
|
|
61
|
+
| `--output <dir>` | `-o`, `-t` | Output directory | `dist` |
|
|
62
|
+
| `--logo <file>` | `-l` | Custom logo image | — |
|
|
63
|
+
| `--single-file` | `-1` | Embed all assets into a single HTML file | `false` |
|
|
64
|
+
| `--embed` | `-e` | Omit `<html>`/`<body>` tags for embedding | `false` |
|
|
65
|
+
| `--quiet` | `-q` | Suppress output | `false` |
|
|
66
|
+
|
|
67
|
+
### Dev server options
|
|
68
|
+
|
|
69
|
+
| Flag | Alias | Description | Default |
|
|
70
|
+
|------|-------|-------------|---------|
|
|
71
|
+
| `--port <port>` | `-p` | Port to listen on | `4400` |
|
|
72
|
+
| `--output <dir>` | `-o` | Build output directory | `.preview` |
|
|
73
|
+
| `--logo <file>` | `-l` | Custom logo image | — |
|
|
74
|
+
|
|
75
|
+
### Examples
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Build docs to a custom directory
|
|
79
|
+
sourcey build api.yaml -o public/docs
|
|
80
|
+
|
|
81
|
+
# Start dev server with live reload
|
|
82
|
+
sourcey dev api.yaml
|
|
83
|
+
|
|
84
|
+
# Validate a spec without building
|
|
85
|
+
sourcey validate api.yaml
|
|
86
|
+
|
|
87
|
+
# Build with a custom logo
|
|
88
|
+
sourcey build api.yaml --logo ./logo.png -o docs/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
Create a `sourcey.json` in your project root to configure branding and theming. CLI flags take precedence over config values.
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"logo": "./logo.png",
|
|
98
|
+
"favicon": "./favicon.ico",
|
|
99
|
+
"theme": {
|
|
100
|
+
"--color-primary": "99 102 241",
|
|
101
|
+
"--font-sans": "'IBM Plex Sans', sans-serif",
|
|
102
|
+
"--sidebar-width": "280px"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Theme variables
|
|
108
|
+
|
|
109
|
+
The `theme` object accepts any CSS custom property defined in the design token system. All color tokens use RGB triplets (`R G B`) consumed via `rgb(var(--token))` with alpha support.
|
|
110
|
+
|
|
111
|
+
#### Layout
|
|
112
|
+
|
|
113
|
+
| Variable | Default | Description |
|
|
114
|
+
|----------|---------|-------------|
|
|
115
|
+
| `--sidebar-width` | `18rem` | Sidebar width |
|
|
116
|
+
| `--header-height` | `7rem` | Header height |
|
|
117
|
+
| `--toc-width` | `19rem` | Table of contents width |
|
|
118
|
+
| `--content-padding` | `2.5rem` | Content area padding |
|
|
119
|
+
| `--content-max-width` | `44rem` | Content max width |
|
|
120
|
+
|
|
121
|
+
#### Colors (RGB triplets)
|
|
122
|
+
|
|
123
|
+
| Variable | Default | Description |
|
|
124
|
+
|----------|---------|-------------|
|
|
125
|
+
| `--color-primary` | `99 102 241` | Primary accent (indigo) |
|
|
126
|
+
| `--color-primary-light` | `129 140 248` | Primary light variant |
|
|
127
|
+
| `--color-primary-dark` | `79 70 229` | Primary dark variant |
|
|
128
|
+
| `--color-background-light` | `255 255 255` | Page background (light) |
|
|
129
|
+
| `--color-background-dark` | `11 12 16` | Page background (dark) |
|
|
130
|
+
| `--color-gray-50` through `--color-gray-950` | — | Full gray scale |
|
|
131
|
+
| `--color-success` | `34 197 94` | Success states |
|
|
132
|
+
| `--color-overlay` | `0 0 0` | Overlay backdrop |
|
|
133
|
+
|
|
134
|
+
#### Method colors
|
|
135
|
+
|
|
136
|
+
| Variable | Default | Description |
|
|
137
|
+
|----------|---------|-------------|
|
|
138
|
+
| `--method-get` | `#16a34a` | GET badge |
|
|
139
|
+
| `--method-post` | `#2563eb` | POST badge |
|
|
140
|
+
| `--method-put` | `#d97706` | PUT badge |
|
|
141
|
+
| `--method-delete` | `#dc2626` | DELETE badge |
|
|
142
|
+
| `--method-patch` | `#9333ea` | PATCH badge |
|
|
143
|
+
|
|
144
|
+
#### Typography
|
|
145
|
+
|
|
146
|
+
| Variable | Default | Description |
|
|
147
|
+
|----------|---------|-------------|
|
|
148
|
+
| `--font-sans` | `'Inter', system-ui, sans-serif` | Body font |
|
|
149
|
+
| `--font-mono` | `'JetBrains Mono', monospace` | Code font |
|
|
150
|
+
|
|
151
|
+
## Programmatic API
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { buildDocs } from 'sourcey';
|
|
155
|
+
|
|
156
|
+
const result = await buildDocs({
|
|
157
|
+
specSource: './api.yaml',
|
|
158
|
+
outputDir: './docs',
|
|
159
|
+
logo: './logo.png',
|
|
160
|
+
favicon: './favicon.ico',
|
|
161
|
+
themeOverrides: {
|
|
162
|
+
'--color-primary': '225 29 72',
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
console.log(result.spec.info.title); // "My API"
|
|
167
|
+
console.log(result.spec.operations.length); // 42
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Options
|
|
171
|
+
|
|
172
|
+
| Option | Type | Description |
|
|
173
|
+
|--------|------|-------------|
|
|
174
|
+
| `specSource` | `string` | Path or URL to the spec file (required) |
|
|
175
|
+
| `outputDir` | `string` | Output directory (default: `"dist"`) |
|
|
176
|
+
| `logo` | `string` | Path to a custom logo |
|
|
177
|
+
| `favicon` | `string` | Path to a custom favicon |
|
|
178
|
+
| `singleFile` | `boolean` | Embed assets into one HTML file |
|
|
179
|
+
| `embeddable` | `boolean` | Omit `<html>`/`<body>` tags |
|
|
180
|
+
| `skipWrite` | `boolean` | Parse and normalize without writing files |
|
|
181
|
+
| `themeOverrides` | `Record<string, string>` | CSS custom property overrides |
|
|
182
|
+
|
|
183
|
+
## Keyboard Shortcuts
|
|
184
|
+
|
|
185
|
+
| Key | Action |
|
|
186
|
+
|-----|--------|
|
|
187
|
+
| `/` or `Ctrl+K` | Open search |
|
|
188
|
+
| `Escape` | Close search or sidebar |
|
|
189
|
+
| `↑` / `↓` | Navigate search results |
|
|
190
|
+
| `Enter` | Go to selected result |
|
|
191
|
+
|
|
192
|
+
## OpenAPI Extensions
|
|
193
|
+
|
|
194
|
+
| Extension | Description |
|
|
195
|
+
|-----------|-------------|
|
|
196
|
+
| `x-sourcey-hide` | Hide a tag or operation from the docs |
|
|
197
|
+
| `x-sourcey-code-samples` | Custom code samples for an operation |
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
git clone https://github.com/sourcey/sourcey.git
|
|
203
|
+
cd sourcey
|
|
204
|
+
npm install
|
|
205
|
+
npm run build
|
|
206
|
+
npm test
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Project structure
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
src/
|
|
213
|
+
cli.ts CLI entry point (citty)
|
|
214
|
+
index.ts Programmatic API
|
|
215
|
+
config.ts sourcey.json loader
|
|
216
|
+
dev-server.ts Dev server with live reload
|
|
217
|
+
core/ Spec processing pipeline
|
|
218
|
+
loader.ts Load from file/URL
|
|
219
|
+
parser.ts Validate and dereference $refs
|
|
220
|
+
converter.ts Swagger 2.0 → OpenAPI 3.x
|
|
221
|
+
normalizer.ts OpenAPI → internal model
|
|
222
|
+
types.ts Internal types
|
|
223
|
+
components/ Preact components (SSG)
|
|
224
|
+
layout/ Page, Sidebar, Head
|
|
225
|
+
openapi/ Operation, Parameters, Responses, etc.
|
|
226
|
+
schema/ SchemaView, ExampleView
|
|
227
|
+
ui/ Badge, Markdown, SectionLabel
|
|
228
|
+
renderer/ HTML generation
|
|
229
|
+
static-renderer.ts Preact → HTML string
|
|
230
|
+
html-builder.ts Assemble HTML + CSS + JS
|
|
231
|
+
context.ts Render context
|
|
232
|
+
client/ Browser JavaScript (vanilla JS)
|
|
233
|
+
sidebar.js Drawer toggle, close on outside click/Escape
|
|
234
|
+
scroll-tracker.js IntersectionObserver nav highlighting
|
|
235
|
+
tabs.js Synced language tab switching
|
|
236
|
+
copy.js Clipboard copy with feedback
|
|
237
|
+
theme-toggle.js Dark/light mode toggle
|
|
238
|
+
search.js Client-side search dialog
|
|
239
|
+
themes/
|
|
240
|
+
default/ Default theme CSS
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Testing
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
npm test # Run all tests
|
|
247
|
+
npm run test:watch # Watch mode
|
|
248
|
+
npm run typecheck # TypeScript type checking
|
|
249
|
+
npm run lint # ESLint
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineCommand, runMain } from "citty";
|
|
3
|
+
import { buildDocs, buildSiteDocs } from "./index.js";
|
|
4
|
+
import { loadConfig, hasNavigation, configFromSpec } from "./config.js";
|
|
5
|
+
const build = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "build",
|
|
8
|
+
description: "Build documentation from an OpenAPI spec or sourcey.json config",
|
|
9
|
+
},
|
|
10
|
+
args: {
|
|
11
|
+
spec: {
|
|
12
|
+
type: "positional",
|
|
13
|
+
description: "Path or URL to the OpenAPI/Swagger spec file (optional if sourcey.json exists)",
|
|
14
|
+
required: false,
|
|
15
|
+
},
|
|
16
|
+
output: {
|
|
17
|
+
type: "string",
|
|
18
|
+
alias: ["o", "t"],
|
|
19
|
+
description: "Output directory",
|
|
20
|
+
default: "dist",
|
|
21
|
+
},
|
|
22
|
+
logo: {
|
|
23
|
+
type: "string",
|
|
24
|
+
alias: ["l"],
|
|
25
|
+
description: "Path to a custom logo file",
|
|
26
|
+
},
|
|
27
|
+
embed: {
|
|
28
|
+
type: "boolean",
|
|
29
|
+
alias: ["e"],
|
|
30
|
+
description: "Generate embeddable output (no <html>/<body> tags)",
|
|
31
|
+
default: false,
|
|
32
|
+
},
|
|
33
|
+
quiet: {
|
|
34
|
+
type: "boolean",
|
|
35
|
+
alias: ["q"],
|
|
36
|
+
description: "Suppress output",
|
|
37
|
+
default: false,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
async run({ args }) {
|
|
41
|
+
const startTime = Date.now();
|
|
42
|
+
const config = await loadConfig();
|
|
43
|
+
try {
|
|
44
|
+
if (args.spec) {
|
|
45
|
+
// Spec file provided: wrap in single-tab site
|
|
46
|
+
if (!args.quiet) {
|
|
47
|
+
console.log(`\nSourcey — generating docs from ${args.spec}\n`);
|
|
48
|
+
}
|
|
49
|
+
const result = await buildDocs({
|
|
50
|
+
specSource: args.spec,
|
|
51
|
+
outputDir: args.output,
|
|
52
|
+
logo: args.logo || config.logo,
|
|
53
|
+
favicon: config.favicon,
|
|
54
|
+
embeddable: args.embed,
|
|
55
|
+
themeOverrides: config.theme,
|
|
56
|
+
});
|
|
57
|
+
if (!args.quiet) {
|
|
58
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
59
|
+
console.log(` Spec: ${result.spec.info.title} v${result.spec.info.version}`);
|
|
60
|
+
console.log(` Operations: ${result.spec.operations.length}`);
|
|
61
|
+
console.log(` Schemas: ${Object.keys(result.spec.schemas).length}`);
|
|
62
|
+
console.log(` Time: ${elapsed}s\n`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (hasNavigation(config)) {
|
|
66
|
+
// sourcey.json with navigation
|
|
67
|
+
if (!args.quiet) {
|
|
68
|
+
console.log(`\nSourcey — building documentation site\n`);
|
|
69
|
+
}
|
|
70
|
+
const result = await buildSiteDocs({
|
|
71
|
+
outputDir: args.output,
|
|
72
|
+
});
|
|
73
|
+
if (!args.quiet) {
|
|
74
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
75
|
+
console.log(` Pages: ${result.pageCount}`);
|
|
76
|
+
console.log(` Output: ${result.outputDir}`);
|
|
77
|
+
console.log(` Time: ${elapsed}s\n`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.error("\nError: No spec file provided and no sourcey.json with navigation found.");
|
|
82
|
+
console.error("Usage: sourcey build <spec-file> or create a sourcey.json with navigation config.\n");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
88
|
+
console.error(`\nError: ${message}\n`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
const dev = defineCommand({
|
|
94
|
+
meta: {
|
|
95
|
+
name: "dev",
|
|
96
|
+
description: "Start a dev server with live reload",
|
|
97
|
+
},
|
|
98
|
+
args: {
|
|
99
|
+
spec: {
|
|
100
|
+
type: "positional",
|
|
101
|
+
description: "Path to the OpenAPI/Swagger spec file (optional if sourcey.json exists)",
|
|
102
|
+
required: false,
|
|
103
|
+
},
|
|
104
|
+
port: {
|
|
105
|
+
type: "string",
|
|
106
|
+
alias: ["p"],
|
|
107
|
+
description: "Port to listen on",
|
|
108
|
+
default: "4400",
|
|
109
|
+
},
|
|
110
|
+
output: {
|
|
111
|
+
type: "string",
|
|
112
|
+
alias: ["o"],
|
|
113
|
+
description: "Output directory for built files",
|
|
114
|
+
default: ".preview",
|
|
115
|
+
},
|
|
116
|
+
logo: {
|
|
117
|
+
type: "string",
|
|
118
|
+
alias: ["l"],
|
|
119
|
+
description: "Path to a custom logo file",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
async run({ args }) {
|
|
123
|
+
const config = await loadConfig();
|
|
124
|
+
const { startDevServer } = await import("./dev-server.js");
|
|
125
|
+
await startDevServer({
|
|
126
|
+
specSource: args.spec || undefined,
|
|
127
|
+
outputDir: args.output,
|
|
128
|
+
port: parseInt(args.port, 10),
|
|
129
|
+
logo: args.logo || config.logo,
|
|
130
|
+
favicon: config.favicon,
|
|
131
|
+
themeOverrides: config.theme,
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const validate = defineCommand({
|
|
136
|
+
meta: {
|
|
137
|
+
name: "validate",
|
|
138
|
+
description: "Validate an OpenAPI/Swagger spec file",
|
|
139
|
+
},
|
|
140
|
+
args: {
|
|
141
|
+
spec: {
|
|
142
|
+
type: "positional",
|
|
143
|
+
description: "Path or URL to the OpenAPI/Swagger spec file",
|
|
144
|
+
required: true,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
async run({ args }) {
|
|
148
|
+
try {
|
|
149
|
+
const result = await buildDocs({
|
|
150
|
+
specSource: args.spec,
|
|
151
|
+
skipWrite: true,
|
|
152
|
+
});
|
|
153
|
+
console.log(`\n ✓ Valid: ${result.spec.info.title} v${result.spec.info.version}`);
|
|
154
|
+
console.log(` Operations: ${result.spec.operations.length}`);
|
|
155
|
+
console.log(` Schemas: ${Object.keys(result.spec.schemas).length}\n`);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
159
|
+
console.error(`\n ✗ Invalid: ${message}\n`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
const main = defineCommand({
|
|
165
|
+
meta: {
|
|
166
|
+
name: "sourcey",
|
|
167
|
+
version: "2.0.0-alpha.1",
|
|
168
|
+
description: "Generate beautiful static API documentation from OpenAPI/Swagger specifications",
|
|
169
|
+
},
|
|
170
|
+
subCommands: {
|
|
171
|
+
build,
|
|
172
|
+
dev,
|
|
173
|
+
validate,
|
|
174
|
+
},
|
|
175
|
+
args: {
|
|
176
|
+
spec: {
|
|
177
|
+
type: "positional",
|
|
178
|
+
description: "Path or URL to the OpenAPI/Swagger spec file (shorthand for 'sourcey build')",
|
|
179
|
+
required: false,
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
async run({ args, rawArgs }) {
|
|
183
|
+
// Skip if a subcommand was matched (citty still calls parent run)
|
|
184
|
+
if (rawArgs.includes("build") || rawArgs.includes("dev") || rawArgs.includes("validate"))
|
|
185
|
+
return;
|
|
186
|
+
if (args.spec) {
|
|
187
|
+
// `sourcey <specfile>` → `sourcey build <specfile>`
|
|
188
|
+
await build.run({
|
|
189
|
+
args: { _: [], spec: args.spec, output: "dist", logo: "", embed: false, quiet: false },
|
|
190
|
+
rawArgs: [],
|
|
191
|
+
cmd: build,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
runMain(main);
|
|
197
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Copy button — clipboard copy with "Copied!" feedback
|
|
2
|
+
(function () {
|
|
3
|
+
document.addEventListener('click', function (e) {
|
|
4
|
+
var btn = e.target.closest('.copy-btn');
|
|
5
|
+
if (!btn) return;
|
|
6
|
+
var wrapper = btn.closest('.code-block-wrapper');
|
|
7
|
+
if (!wrapper) return;
|
|
8
|
+
var code = wrapper.querySelector('code');
|
|
9
|
+
if (!code) return;
|
|
10
|
+
|
|
11
|
+
navigator.clipboard.writeText(code.textContent || '').then(function () {
|
|
12
|
+
var span = btn.querySelector('span');
|
|
13
|
+
if (span) span.textContent = 'Copied!';
|
|
14
|
+
btn.classList.add('copied');
|
|
15
|
+
setTimeout(function () {
|
|
16
|
+
if (span) span.textContent = 'Copy';
|
|
17
|
+
btn.classList.remove('copied');
|
|
18
|
+
}, 2000);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AACA,OAAO,cAAc,CAAC;AACtB,OAAO,qBAAqB,CAAC;AAC7B,OAAO,WAAW,CAAC;AACnB,OAAO,WAAW,CAAC;AACnB,OAAO,mBAAmB,CAAC;AAC3B,OAAO,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,OAAO,cAAc,CAAC;AACtB,OAAO,qBAAqB,CAAC;AAC7B,OAAO,WAAW,CAAC;AACnB,OAAO,WAAW,CAAC;AACnB,OAAO,mBAAmB,CAAC;AAC3B,OAAO,aAAa,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Scroll tracker — highlights the active sidebar nav link as user scrolls.
|
|
2
|
+
//
|
|
3
|
+
// Observes [data-traverse-target] section anchors and toggles .active
|
|
4
|
+
// on the matching #nav .nav-link. All visual styling is in sourcey.css;
|
|
5
|
+
// this file only manages the class.
|
|
6
|
+
(function () {
|
|
7
|
+
var navLinks = document.querySelectorAll('#nav .nav-link');
|
|
8
|
+
var targets = document.querySelectorAll('[data-traverse-target]');
|
|
9
|
+
if (!targets.length || !navLinks.length) return;
|
|
10
|
+
|
|
11
|
+
var currentId = null;
|
|
12
|
+
|
|
13
|
+
// Map traverse-target id → nav link element (by matching href fragment)
|
|
14
|
+
var linkMap = {};
|
|
15
|
+
navLinks.forEach(function (link) {
|
|
16
|
+
var href = link.getAttribute('href');
|
|
17
|
+
if (href && href.indexOf('#') !== -1) {
|
|
18
|
+
linkMap[href.split('#')[1]] = link;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// If no fragment-based links found, bail (e.g. markdown pages)
|
|
23
|
+
if (!Object.keys(linkMap).length) return;
|
|
24
|
+
|
|
25
|
+
function activate(id) {
|
|
26
|
+
if (id === currentId) return;
|
|
27
|
+
currentId = id;
|
|
28
|
+
|
|
29
|
+
navLinks.forEach(function (link) {
|
|
30
|
+
link.classList.remove('active');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
var active = linkMap[id];
|
|
34
|
+
if (active) {
|
|
35
|
+
active.classList.add('active');
|
|
36
|
+
// Scroll the sidebar so the active link stays visible
|
|
37
|
+
active.scrollIntoView({ block: 'nearest', behavior: 'auto' });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Track which sections are visible; pick the topmost one
|
|
42
|
+
var visibleSections = new Map();
|
|
43
|
+
|
|
44
|
+
var observer = new IntersectionObserver(function (entries) {
|
|
45
|
+
entries.forEach(function (entry) {
|
|
46
|
+
var id = entry.target.getAttribute('data-traverse-target');
|
|
47
|
+
if (entry.isIntersecting) {
|
|
48
|
+
visibleSections.set(id, entry.target);
|
|
49
|
+
} else {
|
|
50
|
+
visibleSections.delete(id);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Pick the topmost visible section (smallest boundingClientRect.top)
|
|
55
|
+
var best = null;
|
|
56
|
+
var bestTop = Infinity;
|
|
57
|
+
visibleSections.forEach(function (el, id) {
|
|
58
|
+
var top = el.getBoundingClientRect().top;
|
|
59
|
+
if (top < bestTop) { bestTop = top; best = id; }
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Fallback: if nothing visible, find the last section above viewport
|
|
63
|
+
if (!best) {
|
|
64
|
+
for (var i = targets.length - 1; i >= 0; i--) {
|
|
65
|
+
if (targets[i].getBoundingClientRect().top < 10) {
|
|
66
|
+
best = targets[i].getAttribute('data-traverse-target');
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (best) activate(best);
|
|
73
|
+
}, {
|
|
74
|
+
// Trigger when section header enters top 20% of viewport
|
|
75
|
+
rootMargin: '0px 0px -80% 0px',
|
|
76
|
+
threshold: 0
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
targets.forEach(function (el) { observer.observe(el); });
|
|
80
|
+
|
|
81
|
+
// Activate on initial load based on URL hash or first section
|
|
82
|
+
var hash = window.location.hash.slice(1);
|
|
83
|
+
if (hash && linkMap[hash]) {
|
|
84
|
+
activate(hash);
|
|
85
|
+
} else if (targets.length) {
|
|
86
|
+
activate(targets[0].getAttribute('data-traverse-target'));
|
|
87
|
+
}
|
|
88
|
+
})();
|