Repository: swagger-api/swagger-editor
Stars: 9430
CLAUDE.md
CLAUDE.md - SwaggerEditor Codebase Guide for AI Assistants
Version: 5.0.6 | Last Updated: 2026-02-27
---
Project Overview
SwaggerEditor is a browser-based editor for API specifications supporting OpenAPI 2.0/3.0/3.1, AsyncAPI 2.x, API Design Systems, and JSON Schema. Built as a React app on SwaggerUI's plugin architecture with a split-pane Monaco Editor interface.
Core Technologies: React 17+/18, SwaggerUI React, TypeScript (gradual), Immutable.js, Monaco Editor, ApiDOM, Webpack 5, Jest + Playwright
Philosophy: Plugin-based architecture, minimal over-engineering, heavy E2E testing, gradual TypeScript adoption, app/ESM/UMD build artifacts.
---
Codebase Structure
swagger-editor/
├── config/ # Webpack configs (dev, prod, bundle), Jest transforms
├── docs/ # architecture.md, customization guides, migration guides
├── public/ # Static assets, HTML template
├── scripts/ # Build scripts (start, build, test)
├── src/
│ ├── App.tsx # Main app component & plugin composition
│ ├── index.tsx # Browser entry point
│ ├── plugins/ # 26 plugins (see Architecture section)
│ ├── presets/
│ │ ├── monaco/ # Full-featured preset (default)
│ │ └── textarea/ # Lightweight fallback
│ ├── styles/ # Global SCSS (index.scss)
│ └── types/ # TypeScript declarations (*.d.ts)
├── test/
│ ├── playwright/
│ │ ├── e2e/ # Test specs (*.spec.ts)
│ │ ├── fixtures/ # Test data
│ │ ├── helpers/ # Helper functions
│ │ └── tsconfig.json
│ └── setupTests.js # Jest setup (jest-dom, canvas-mock)
├── build/ # Standalone app (generated)
└── dist/esm|umd|types/ # Library bundles (generated)---
Architecture & Design Patterns
Plugin Categories (26 total)
Editor implementations:
- editor-textarea — HTML <textarea> fallback
- editor-monaco — Monaco Editor (advanced)
- editor-monaco-language-apidom — ApiDOM language support
- editor-monaco-yaml-paste — YAML paste transformations
Preview plugins:
- editor-preview — Base preview component
- editor-preview-swagger-ui — OpenAPI rendering
- editor-preview-asyncapi — AsyncAPI rendering
- editor-preview-api-design-systems — ADS rendering
Editor support plugins:
- editor-content-type — Auto-detect content type (OpenAPI/AsyncAPI/JSON Schema)
- editor-content-persistence — LocalStorage persistence
- editor-content-read-only — Read-only mode
- editor-content-origin — Track content source (URL, file, user)
- editor-content-fixtures — Load example/fixture files
- editor-content-from-file — File import
Generic feature plugins:
- layout, top-bar, modals, dialogs, dropdown-menu, dropzone, splash-screen, editor-safe-render, swagger-ui-adapter, util, versions, props-change-watcher
Component Hierarchy
App.tsx (SwaggerUI wrapper)
└── SwaggerEditorLayout
├── SplashScreen
├── TopBar (File/Edit/Generate menus)
└── Container
└── Dropzone
└── SplitPane (resizable)
├── EditorPane
│ ├── EditorPaneBarTop
│ ├── MonacoEditor / TextareaEditor
│ └── ValidationPane (errors/warnings)
└── EditorPreviewPane
└── EditorPreviewSwaggerUI / AsyncAPI / ApiDesignSystemsDesign Patterns
- Container/Presenter: Containers connect to Redux (e.g., MonacoEditorContainer.jsx), presenters handle rendering (e.g., MonacoEditor.jsx)
- HOC via wrapComponents: Plugins wrap existing components to enhance without forking
- getComponent: Dynamically resolve components — const C = getComponent('MonacoEditor')
- FSM for async: idle → loading → success/failure with request ID tracking to prevent race conditions
---
Plugin System
Plugin Structure
// src/plugins/plugin-name/index.js
const PluginName = ({ getSystem }) => ({
afterLoad: function, // Runs after plugin loads
components: {
ComponentName: Component, // Register new components
},
wrapComponents: {
ComponentName: WrapperFn, // Wrap/enhance existing components
},
rootInjects: {
utilityName: function, // Inject utilities into system
},
statePlugins: {
pluginStateKey: {
actions: {}, // Action creators
reducers: {}, // Immutable.js reducers
selectors: {}, // Reselect selectors
wrapActions: {}, // Action middleware
},
},
fn: { utilityFunction: function },
});
export default PluginName;Typical Plugin File Structure
plugin-name/
├── index.js
├── actions/index.js
├── reducers.js
├── selectors.js
├── components/ComponentName.jsx
├── components/ComponentName.scss
├── extensions/other-plugin/wrap-components/ComponentWrapper.jsx
├── after-load.js
└── fn.jsComponent Wrapping Pattern
// extensions/editor-preview/wrap-components/EditorPreviewWrapper.jsx
const EditorPreviewWrapper = (Original, system) => {
const EnhancedComponent = (props) => {
const isOpenAPI = system.editorSelectors.selectIsContentTypeOpenAPI();
if (isOpenAPI) return <EditorPreviewSwaggerUI />;
return <Original {...props} />;
};
return EnhancedComponent; // must return new component, not Original directly
};
export default EditorPreviewWrapper;System Access in Plugins
const MyPlugin = (system) => {
const { getComponent, editorActions, editorSelectors, fn } = system;
const content = editorSelectors.selectEditorContent();
editorActions.setEditorContent('new content');
const MonacoEditor = getComponent('MonacoEditor');
};---
Development Workflows
Prerequisites
- Node.js
>=22.11.0, npm >=10.9.0, Python 3.x (node-gyp), GLIBC >=2.29- Optional: Docker or emscripten (for WASM builds)
npm Scripts
| Script | Description |
|--------|-------------|
| npm start | Dev server on port 3000 (hot reload) |
| npm test | Jest unit tests (watch mode) |
| npm run lint | ESLint on all files |
| npm run lint:fix | Auto-fix ESLint errors |
| npm run build | Build all artifacts (app + bundles + types) |
| npm run build:app | Standalone app → /build |
| npm run build:app:serve | Serve built app on port 3050 |
| npm run build:bundle:esm | ESM bundle → /dist/esm |
| npm run build:bundle:umd | UMD bundle → /dist/umd |
| npm run build:definitions | TypeScript definitions → /dist/types |
| npx playwright test | E2E tests (headless) |
| npx playwright test --headed | E2E with browser visible |
| npx playwright test --ui | Interactive UI mode |
| npx playwright test --debug | Debug mode |
| npx playwright show-report | View test report |
| npm run clean | Remove /build and /dist |
Environment Variables (.env, baked into build)
| Variable | Description |
|----------|-------------|
| REACT_APP_DEFINITION_FILE | Local file path (must be in /public/static) |
| REACT_APP_DEFINITION_URL | Remote URL (takes precedence over file) |
| REACT_APP_VERSION | App version (from package.json) |
| REACT_APP_APIDOM_WORKER_FILENAME | ApiDOM worker filename |
| REACT_APP_EDITOR_WORKER_FILENAME | Monaco editor worker filename |
Web Workers
Two workers handle background processing: apidom.worker.js (parsing/validation) and editor.worker.js (Monaco ops). Configure Monaco env before rendering:
self.MonacoEnvironment = { baseUrl: ${document.baseURI || location.href}dist/ };Workers must be accessible at runtime — either build them separately via webpack entry points, or copy pre-built files from node_modules/swagger-editor/dist/umd/ using CopyWebpackPlugin.
OOM Fix for Large Builds
export NODE_OPTIONS="--max_old_space_size=4096"
npm run build---
Testing Strategy
Unit Testing (Jest)
- Location:
src//*.{spec,test}.{js,jsx,ts,tsx}, run with npm test- ⚠️ Only 1 unit test exists:
ValidationPane.test.jsx — heavy reliance on E2EE2E Testing (Playwright)
- Location:
test/playwright/e2e/*.spec.ts, base URL http://localhost:3000- All tests written in TypeScript with full
@playwright/test type safetyExisting test files: app.spec.ts, plugin.top-bar.spec.ts, plugin.editor-monaco.spec.ts, plugin.editor-monaco-yaml-paste.spec.ts, plugin.dropzone.spec.ts, plugin.validation-pane.spec.ts, plugin.editor-content-from-file.spec.ts, plugin.editor-persistence.spec.ts, plugin.editor-preview-*.spec.ts, and more.
Helper functions (test/playwright/helpers/):
- Setup: visitBlankPage(), waitForSplashScreen(), prepareAsyncAPI()
- Editor: typeInEditor(), getAllEditorText(), selectAllEditorText()
- Menu: clickMenu(), loadExample(), generateServer()
E2E test template:
import { test, expect } from '@playwright/test';
import { visitBlankPage, waitForSplashScreen } from '../helpers';test.describe('Feature Name', () => {
test.beforeEach(async ({ page }) => {
await visitBlankPage(page);
await waitForSplashScreen(page);
});
test('should do something', async ({ page }) => {
await page.locator('[data-testid="some-element"]').click();
await expect(page.locator('text=Expected Text')).toBeVisible();
});
});
---
Code Style & Conventions
ESLint (Airbnb + Prettier + jsx-a11y + @typescript-eslint)
Key rules:
- Arrow functions for named components — not function declarations
- File extensions required on all JS/JSX imports: ./Component.jsx ✅ ./Component ❌ (.ts/.tsx exempt)
- JSX only in .jsx/.tsx files
- Import groups: external/builtin first (blank line), then internal
Fix violations: npm run lint:fix
Prettier
printWidth: 100, tabWidth: 2, semi: true, singleQuote: true, trailingComma: 'es5', endOfLine: 'lf'Commit Messages (Conventional Commits)
<type>(<scope>): <subject> ← max 69 charactersTypes: feat, fix, docs, style, refactor, test, chore, perf, ci
Scopes: plugin name (editor-monaco, top-bar, validation) or area (deps, build, release)
Enforced by commitlint via Husky pre-commit hook.
File Naming
| Type | Convention | Example |
|------|-----------|---------|
| React Components | PascalCase.jsx/tsx | MonacoEditor.jsx |
| Utilities | kebab-case.js | import-url.js |
| Types | kebab-case.d.ts | system.d.ts |
| Styles (partials) | _kebab-case.scss | _monaco-editor.scss |
| Unit Tests | ComponentName.test.jsx | ValidationPane.test.jsx |
| E2E Tests | feature.spec.ts | plugin.editor-monaco.spec.ts |
Styling
- SCSS for all component styles; BEM-like naming (
.editor-pane__title)- Partial files prefixed with
_; global styles in /src/styles/index.scssTypeScript
- Strict mode off globally; gradual adoption via
typescript-strict-plugin- Legacy files may use
// @ts-strict-ignore at top; new files should aim for strictness-
allowJs: false — TypeScript files only in tsconfig scope- Path aliases work:
import X from 'plugins/editor-monaco' ✅---
State Management
SwaggerUI's plugin-based Redux-like system with Immutable.js.
Actions
export const SET_EDITOR_CONTENT = 'editor_set_content';
export const setEditorContent = (content) => ({ type: SET_EDITOR_CONTENT, payload: content });// Async thunk
export const loadDefinition = (url) => async (system) => {
const { editorActions, fn } = system;
const requestId = generateRequestId();
editorActions.loadDefinitionRequest({ url, requestId });
try {
const content = await fn.fetchUrl(url);
editorActions.loadDefinitionSuccess({ content, requestId });
} catch (error) {
editorActions.loadDefinitionFailure({ error, requestId });
}
};
Reducers (Immutable.js)
import { Map } from 'immutable';export const initialState = Map({ content: '', status: 'idle', error: null, requestId: null });
const loadSuccessReducer = (state, action) => {
if (state.get('requestId') !== action.payload.requestId) return state; // ignore stale
return state.merge({ status: 'success', content: action.payload.content, error: null });
};
export default {
[SET_EDITOR_CONTENT]: (state, action) => state.set('content', action.payload),
[LOAD_DEFINITION_SUCCESS]: loadSuccessReducer,
};
Selectors (Reselect)
import { createSelector } from 'reselect';export const selectEditorState = (state) => state.get('editor');
export const selectEditorContent = (state) => selectEditorState(state).get('content');
export const selectStatus = (state) => selectEditorState(state).get('status');
// Always memoize derived state
export const selectValidationErrors = createSelector(
selectValidationResults,
(results) => results.filter((r) => r.severity === 'error')
);
State Access in Components
const MyComponent = () => {
const { editorSelectors, editorActions } = useSystem();
const content = editorSelectors.selectEditorContent();
const isLoading = editorSelectors.selectIsLoading(); return <div>{isLoading ? 'Loading...' : content}</div>;
};
Known Issue: Editor Content Storage
Editor content is stored in SwaggerUI spec plugin, causing parse/resolve/store on every keystroke → typing lag with large specs. Future fix: store content in editor plugin with FSM pattern in preview plugins.- Avoid unnecessary state updates in the editor
- Debounce expensive validation triggers
---
Common Tasks
Creating a New Plugin
// src/plugins/my-plugin/index.js
const MyPlugin = () => ({
components: { MyComponent: () => <div>Hello from MyPlugin</div> },
statePlugins: {
myPlugin: {
initialState: Map({ data: null }),
actions: { myAction: (payload) => ({ type: 'MY_ACTION', payload }) },
reducers: { MY_ACTION: (state, action) => state.set('data', action.payload) },
selectors: { selectData: createSelector((s) => s.get('myPlugin'), (s) => s.get('data')) },
},
},
});
export default MyPlugin;Then import and add to App.tsx or your preset's plugin array.
Adding a Component Wrapper
// src/plugins/my-plugin/extensions/top-bar/wrap-components/TopBarWrapper.jsx
const TopBarWrapper = (Original, system) => {
const Enhanced = (props) => {
const showBanner = system.myPluginSelectors.selectShowBanner();
return (
<>
{showBanner && <div className="banner">Important Notice</div>}
<Original {...props} />
</>
);
};
return Enhanced;
};
// Register in plugin: wrapComponents: { TopBar: TopBarWrapper }Adding a New Content Type
// Extend detection in editor-content-type plugin (order matters — specific first)
const detectContentType = (content) => {
if (/^openapi:\s*["']?3\.1/.test(content)) return 'openapi-3-1';
if (/^openapi:\s*["']?3\.0/.test(content)) return 'openapi-3-0';
if (/^swagger:\s*["']?2\.0/.test(content)) return 'openapi-2-0';
if (/^asyncapi:\s*["']?2\./.test(content)) return 'asyncapi-2';
if (/^myspec:\s*["']?1\.0/.test(content)) return 'myspec-1-0'; // custom
return 'unknown';
};Then create a preview plugin that wraps EditorPreview and conditionally renders based on editorSelectors.selectEditorContentType().
Adding E2E Tests
// test/playwright/e2e/plugin.my-feature.spec.ts
import { test, expect } from '@playwright/test';
import { visitBlankPage, waitForSplashScreen } from '../helpers';test.describe('My Feature', () => {
test.beforeEach(async ({ page }) => {
await visitBlankPage(page);
await waitForSplashScreen(page);
});
test('should perform action', async ({ page }) => {
await page.locator('[data-testid="my-button"]').click();
await expect(page.locator('text=Expected Result')).toBeVisible();
});
});
Debugging Validation
const { editorSelectors } = useSystem();
console.log('markers:', editorSelectors.selectEditorMarkers());
console.log('diagnostics:', editorSelectors.selectDiagnostics());
console.log('content type:', editorSelectors.selectEditorContentType());
console.log('is OpenAPI:', editorSelectors.selectIsContentTypeOpenAPI());---
Important Gotchas
1. File Extensions Required
import Component from './Component.jsx'; // ✅
import Component from './Component'; // ❌ fails lintingException:
.ts/.tsx files may omit extensions due to ESLint overrides.2. Immutable.js State Updates
state.data = newValue; // ❌ mutates state
return state.set('data', val); // ✅
return state.merge({ a, b }); // ✅ multiple fields3. Component Wrapping Return Value
const Wrapper = (Original, system) => Original; // ❌ no enhancement
const Wrapper = (Original, system) => (props) => <Original {...props} />; // ✅4. Request ID Race Conditions
Always include requestId in async actions and guard in reducers:
if (state.get('requestId') !== action.payload.requestId) return state;5. Monaco Environment Configuration
Must be set before rendering SwaggerEditor:
self.MonacoEnvironment = { baseUrl: ${document.baseURI || location.href}dist/ };
ReactDOM.render(<SwaggerEditor />, document.getElementById('root'));6. Web Worker Path Issues
Workers must be accessible at runtime — build separately via webpack entries or copy pre-built files with CopyWebpackPlugin.
7. Large Bundle / OOM Errors
export NODE_OPTIONS="--max_old_space_size=4096"8. Content Type Detection Order
More specific patterns must come first — detect 3.1 before 3.0, 3.0 before 2.0.
9. Selector Memoization
// ❌ recalculates every render
export const selectErrors = (state) => selectResults(state).filter(r => r.severity === 'error');// ✅ memoized with createSelector
export const selectErrors = createSelector(selectResults, (r) => r.filter(...));
10. TypeScript Strict Mode
Off globally. Use typescript-strict-plugin per file. New files should aim for strictness (no @ts-strict-ignore). Legacy files may have it.
---
Key Files Reference
Core:
| File | Purpose |
|------|---------|
| /src/App.tsx | Main component, plugin composition |
| /src/index.tsx | Browser entry point |
| /public/index.html | HTML template, MonacoEnvironment setup |
Config:
| File | Purpose |
|------|---------|
| /package.json | Dependencies, scripts, Jest config |
| /tsconfig.json | TypeScript compiler options |
| /.eslintrc | ESLint rules (Airbnb + Prettier) |
| /.prettierrc | Formatting rules |
| /.commitlintrc.json | Commit message linting |
| /config/webpack.config.js | Webpack configuration |
| /playwright.config.ts | Playwright configuration |
Docs:
| File | Purpose |
|------|---------|
| /docs/architecture.md | High-level architecture overview |
| /docs/customization/plug-points/ | Plugin customization guides |
| /docs/migration*.md | Migration guides from legacy version |
Testing:
| File | Purpose |
|------|---------|
| /test/setupTests.js | Jest test setup |
| /test/playwright/e2e/*.spec.ts | E2E test specs |
| /test/playwright/helpers/ | Playwright helper functions |
Plugin Paths:
- Editor: /src/plugins/editor-textarea/, /src/plugins/editor-monaco/, /src/plugins/editor-monaco-language-apidom/, /src/plugins/editor-monaco-yaml-paste/
- Preview: /src/plugins/editor-preview*/
- Support: /src/plugins/editor-content-*/
- Generic: /src/plugins/layout/, /src/plugins/top-bar/, /src/plugins/modals/, etc.
- Presets: /src/presets/monaco/ (default), /src/presets/textarea/
---
Quick Reference for AI Assistants
Fixing a Bug
1. Identify the plugin — most bugs are plugin-specific
2. Check
test/playwright/e2e/plugin.<name>.spec.ts for existing coverage3. Review actions/reducers/selectors in that plugin
4. Add an E2E test to prevent regression
Adding a Feature
1. Identify affected plugins; decide new vs. extend existing
2. Plan state management needs (new actions/reducers?)
3. Use component wrapping to enhance without forking
4. Write E2E test first (TDD preferred); update README if user-facing
Refactoring
1. Follow established patterns — don't tightly couple plugins
2. Maintain Immutable.js correctness and selector memoization
3. Run:
npm run lint:fix && npm test && npx playwright testWhen Stuck
1. Read the relevant plugin source — most logic lives there
2. Check
docs/architecture.md for high-level overview3. Look at similar plugins for patterns to copy
4. Check Playwright tests to see how features are exercised
Before Committing
- [ ]
npm run lint:fix- [ ]
npm test- [ ]
npx playwright test- [ ] Commit message: Conventional Commits format, max 69 chars header
- [ ]
npm run build succeeds---
Last Updated: 2026-02-27 | Version: 5.0.6 | Update this file when architecture changes significantly
README.md
SwaggerEditor
Table of Contents
- Anonymized analytics
- Getting started
- Prerequisites
- Installation
- Usage
- Development
- Prerequisites
- Setting up
- npm scripts
- Build artifacts
- Package mapping
- Documentation
- Docker
- License
- Software Bill Of Materials (SBOM)
Anonymized analytics
Swagger Editor uses Scarf to collect anonymized installation analytics. These analytics help support the maintainers of this library and ONLY run during installation. To opt out, you can set the scarfSettings.enabled field to false in your project's package.json:
// package.json
{
// ...
"scarfSettings": {
"enabled": false
}
// ...
}Alternatively, you can set the environment variable SCARF_ANALYTICS to false as part of the environment that installs your npm packages, e.g., SCARF_ANALYTICS=false npm install.
Getting started
Prerequisites
These prerequisites are required both for installing SwaggerEditor as a npm package and local development setup.
- node-gyp with Python 3.x
- GLIBC >=2.29
- emscripten or docker needs to be installed, we recommend going with a docker option
Installation
Assuming prerequisites are already installed, SwaggerEditor npm package is installable and works with Node.js >= 12.22.0.
You can install SwaggerEditor via npm CLI by running the following command:
$ npm install swagger-editor@alphaNOTE: when using bundler to build your project which is using swagger-editor@5 npm package,
you might run into following Node.js error: Reached heap limit Allocation failed - JavaScript heap out of memory.
It's caused by significant amount of code that needs to be bundled. This error can be resolved
by extending the Node.js max heap limit: export NODE_OPTIONS="--max_old_space_size=4096".Usage
Use the package in your application:
index.js:
jsimport React from 'react';
import ReactDOM from 'react-dom';
import SwaggerEditor from 'swagger-editor';
import 'swagger-editor/swagger-editor.css';const url = "https://raw.githubusercontent.com/asyncapi/spec/v2.2.0/examples/streetlights-kafka.yml";
const MyApp = () => (
<div>
<h1>SwaggerEditor Integration</h1>
<SwaggerEditor url={url} />
</div>
);
self.MonacoEnvironment = {
/
* We're building into the dist/ folder. When application starts on
* URL=https://example.com then SwaggerEditor will look for
*
apidom.worker.js on https://example.com/dist/apidom.worker.js and
* editor.worker on https://example.com/dist/editor.worker.js.
*/
baseUrl: ${document.baseURI || location.href}dist/,
}ReactDOM.render(<MyApp />, document.getElementById('swagger-editor'));
webpack.config.js (webpack@5)
Install dependencies needed for webpack@5 to properly build SwaggerEditor.
sh$ npm i stream-browserify --save-dev
$ npm i https-browserify --save-dev
$ npm i stream-http --save-dev
$ npm i util --save-dev
$ npm i buffer --save-dev
$ npm i process --save-dev
jsconst path = require('path');
const webpack = require('webpack');module.exports = {
mode: 'production',
entry: {
app: './index.js',
'apidom.worker': 'swagger-editor/apidom.worker',
'editor.worker': 'swagger-editor/editor.worker',
},
output: {
globalObject: 'self',
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
fallback: {
path: false,
fs: false,
http: require.resolve('stream-http'), // required for asyncapi parser
https: require.resolve('https-browserify'), // required for asyncapi parser
stream: require.resolve('stream-browserify'),
util: require.resolve('util'),
url: require.resolve('url'),
buffer: require.resolve('buffer'),
zlib: false,
},
alias: {
// This alias make sure we don't pull two different versions of monaco-editor
'monaco-editor': '/node_modules/monaco-editor',
// This alias makes sure we're avoiding a runtime error related to this package
'@stoplight/ordered-object-literal$': '/node_modules/@stoplight/ordered-object-literal/src/index.mjs',
'react/jsx-runtime.js': 'react/jsx-runtime',
},
},
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: ['process'],
}),
],
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
/
* The default way in which webpack loads wasm files won’t work in a worker,
* so we will have to disable webpack’s default handling of wasm files and
* then fetch the wasm file by using the file path that we get using file-loader.
*
* Resource: https://pspdfkit.com/blog/2020/webassembly-in-a-web-worker/
*
* Alternatively, WASM file can be bundled directly into JavaScript bundle as data URLs.
* This configuration reduces the complexity of WASM file loading
* but increases the overal bundle size:
*
* {
* test: /\.wasm$/,
* type: 'asset/inline',
* }
*/
{
test: /\.wasm$/,
loader: 'file-loader',
type: 'javascript/auto', // this disables webpacks default handling of wasm
},
]
}
};
Alternative webpack.config.js (webpack@5)
We've already built Web Workers artifacts for you, and they're located inside our npm distribution
package in
dist/umd/ directory. To avoid the complexity of building the Web Worker artifacts, you can
use those artifacts directly. This setup will work both for production and development (webpack-dev-server)
and will significantly shorten your build process.Install
copy-webpack-plugin and other needed dependencies.sh$ npm i copy-webpack-plugin --save-dev
$ npm i stream-browserify --save-dev
$ npm i https-browserify --save-dev
$ npm i stream-http --save-dev
$ npm i util --save-dev
$ npm i buffer --save-dev
$ npm i process --save-dev
jsconst path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');module.exports = {
mode: 'production',
entry: {
app: './index.js',
},
output: {
globalObject: 'self',
filename: 'static/js/[name].js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
fallback: {
path: false,
fs: false,
http: require.resolve('stream-http'), // required for asyncapi parser
https: require.resolve('https-browserify'), // required for asyncapi parser
stream: require.resolve('stream-browserify'),
util: require.resolve('util'),
url: require.resolve('url'),
buffer: require.resolve('buffer'),
zlib: false,
},
alias: {
// This alias make sure we don't pull two different versions of monaco-editor
'monaco-editor': '/node_modules/monaco-editor',
// This alias makes sure we're avoiding a runtime error related to this package
'@stoplight/ordered-object-literal$': '/node_modules/@stoplight/ordered-object-literal/src/index.mjs',
'react/jsx-runtime.js': 'react/jsx-runtime',
}
},
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: ['process'],
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'node_modules/swagger-editor/dist/umd/apidom.worker.js',
to: 'static/js',
},
{
from: 'node_modules/swagger-editor/dist/umd/editor.worker.js',
to: 'static/js',
}
]
}),
],
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
/
* The default way in which webpack loads wasm files won’t work in a worker,
* so we will have to disable webpack’s default handling of wasm files and
* then fetch the wasm file by using the file path that we get using file-loader.
*
* Resource: https://pspdfkit.com/blog/2020/webassembly-in-a-web-worker/
*
* Alternatively, WASM file can be bundled directly into JavaScript bundle as data URLs.
* This configuration reduces the complexity of WASM file loading
* but increases the overal bundle size:
*
* {
* test: /\.wasm$/,
* type: 'asset/inline',
* }
*/
{
test: /\.wasm$/,
loader: 'file-loader',
type: 'javascript/auto', // this disables webpacks default handling of wasm
},
]
}
};
Development
Prerequisites
Assuming prerequisites are already installed, Node.js
>=24.14.0 and npm >=11.9.0
are the minimum required versions that this repo runs on, but we recommend using the latest version of Node.js@24.Setting up
If you use nvm, running following command inside this repository
will automatically pick the right Node.js version for you:
sh$ nvm use
Run the following commands to set up the repository for local development:
sh$ git clone https://github.com/swagger-api/swagger-editor.git
$ cd swagger-editor
$ npm i
$ npm start
npm scripts
Lint
sh$ npm run lint
Runs unit and integration tests
sh$ npm test
Runs E2E tests
Usage in development environment:
sh$ npx playwright test --headed # Run with browser visible
$ npx playwright test --ui # Run with Playwright UI
$ npx playwright test --debug # Run in debug mode
Usage in Continuous Integration (CI) environment:
sh$ npx playwright test # Run all tests headless
View test report:
sh$ npx playwright show-report test/playwright/report
Build
sh$ npm run build
This script will build all the SwaggerEditor build artifacts - app, esm and umd.
Build artifacts
After building artifacts, every two new directories will be created: build/ and dist/.
build/
$ npm run build:app
$ npm run build:app:serveBuilds and serves standalone SwaggerEditor application and all it's assets on http://localhost:3050/.
dist/esm/
$ npm run build:bundle:esmThis bundle is suited for consumption by 3rd parties,
which want to use SwaggerEditor as a library in their own applications and have their own build process.
dist/umd/
$ npm run build:bundle:umdSwaggerEditor UMD bundle exports SwaggerEditor symbol on a global object.
It's bundled with React defined as external. This allows the consumer to use his own version of React + ReactDOM and mount SwaggerEditor lazily.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta
name="description"
content="SwaggerEditor"
/>
<title>SwaggerEditor</title>
<link rel="stylesheet" href="./swagger-editor.css" />
</head>
<body>
<div id="swagger-editor"></div>
<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
<script src="./dist/umd/swagger-editor.js"></script>
<script>
const props = {
url: 'https://raw.githubusercontent.com/asyncapi/spec/v2.2.0/examples/streetlights-kafka.yml',
};
const element = React.createElement(SwaggerEditor, props);
const domContainer = document.querySelector('#swagger-editor'); ReactDOM.render(element, domContainer);
</script>
</body>
</html>
npm
SwaggerEditor is released as swagger-editor@5 npm package on npmjs.com.
Package can also be produced manually by running the following commands (assuming you're already followed setting up steps):
$ npm run build:bundle:esm
$ npm run build:bundle:umd
$ npm packPackage mapping
SwaggerEditor maps its build artifacts in package.json file in the following way:
"unpkg": "./dist/umd/swagger-editor.js",
"module": "./dist/esm/swagger-editor.js",
"browser": "./dist/esm/swagger-editor.js",
"jsnext:main": "./dist/esm/swagger-editor.js",
"exports": {
"./package.json": "./package.json",
"./swagger-editor.css": "./dist/swagger-editor.css",
".": {
"browser": "./dist/esm/swagger-editor.js"
},
"./plugins/*": {
"browser": "./dist/esm/plugins/*/index.js",
"node": "./dist/esm/plugins/*/index.js"
},
"./presets/*": {
"browser": "./dist/esm/presets/*/index.js"
},
"./apidom.worker": {
"browser": "./dist/esm/apidom.worker.js"
},
"./editor.worker": {
"browser": "./dist/esm/editor.worker.js"
}
}To learn more about these fields please refer to webpack mainFields documentation
or to Node.js Modules: Packages documentation.
Documentation
Using older version of React
By older versions we specifically refer to React >=17 <18.
By default swagger-editor@5 npm package comes with latest version of React@18.
It's possible to use _swagger-editor@5_ npm package with older version of React.
Let's say my application integrates with _swagger-editor@5_ npm package and uses [email protected].
npm
In order to inform swagger-editor@5 npm package that I require it to use my React version, I need to use npm overrides.
{
"dependencies": {
"react": "=17.0.2",
"react-dom": "=17.0.2"
},
"overrides": {
"swagger-editor": {
"react": "$react",
"react": "$react-dom",
"react-redux": "^8"
}
}
}The React and ReactDOM override are defined as a reference to the dependency. Since _react-redux@9_ only supports React >= 18, we need to use _react-redux@8_.
yarn
In order to inform swagger-editor@5 npm package that I require it to use my specific React version, I need to use yarn resolutions.
{
"dependencies": {
"react": "17.0.2",
"react-dom": "17.0.2"
},
"resolutions": {
"swagger-editor/react": "17.0.2",
"swagger-editor/react-dom": "17.0.2",
"swagger-editor/react-redux": "^8"
}
}The React and ReactDOM resolution cannot be defined as a reference to the dependency. Unfortunately yarn does not support aliasing like $react or $react-dom as npm does. You'll need to specify the exact versions.
Customization
#### Syntax Highlighting Modes
SwaggerEditor supports two syntax highlighting modes for the Monaco editor:
1. Simplified Mode (default) - Regex-based syntax highlighting using Monaco's Monarch tokenizer
2. ApiDOM Mode - Semantic token highlighting provided by ApiDOM Language Service
The simplified mode is enabled by default. If you need more sophisticated semantic highlighting, you can enable ApiDOM mode.
Using Simplified Mode (default):
import EditorMonacoLanguageApiDOMPlugin from 'swagger-editor/plugins/editor-monaco-language-apidom';// Default behavior - uses simplified syntax highlighting
const plugins = [
EditorMonacoLanguageApiDOMPlugin,
// ... other plugins
];
Enabling ApiDOM Mode:
import EditorMonacoLanguageApiDOMPlugin from 'swagger-editor/plugins/editor-monaco-language-apidom';// Enable ApiDOM semantic token highlighting
const plugins = [
EditorMonacoLanguageApiDOMPlugin({ useApiDOMSyntaxHighlighting: true }),
// ... other plugins
];
Visual Differences:
The two modes produce different syntax highlighting appearances:
- Simplified mode:
- Uses regex-based Monarch tokenizer for syntax coloring
- Keywords, strings, numbers, and booleans each have distinct colors
- Does not colorize bracket pairs (brackets are styled as part of the overall token)
- Color scheme defined by theme token rules: plain.keyword, plain.value.string, plain.value.number, plain.value.boolean
- ApiDOM mode:
- Uses semantic token analysis from ApiDOM Language Service
- Provides context-aware token coloring based on specification structure
- Enables bracket pair colorization (semantic tokens don't include bracket information, so editor's bracket colorization feature is enabled)
- Color scheme uses ApiDOM-specific token types with more granular semantic categories
Both modes support:
- OpenAPI 2.0, 3.0, 3.1, 3.2
- AsyncAPI 2.x, 3.x
- JSON and YAML syntax
- Specification extensions (x- prefixed fields)
- Inline JSON objects and arrays
Environment Variables
It is possible to use an environment variable to specify a local JSON/YAML file or a remote URL for SwaggerEditor to load on startup.
These environment variables will get baked in during build time into build artifacts.
Environment variables currently available:
| Variable name | Description |
|-----------------------------|:----------------------------------------------------------------------------------------------------------:|
| REACT_APP_DEFINITION_FILE | Specifies a local file path, and the specified file must also be present in the /public/static directory |
| REACT_APP_DEFINITION_URL | Specifies a remote URL. This environment variable currently takes precedence over REACT_APP_SWAGGER_FILE |
| REACT_APP_VERSION | Specifies the version of this app. The version is read from package.json file. |
Sample environment variable values can be found in .env file. For more information about using
environment variables, please refer to adding Custom Environment Variables
section of Create React App documentation.
Using preview plugins in SwaggerUI
SwaggerEditor comes with number of preview plugins that are responsible for rendering
the definition that's being created in the editor. These plugins include:
- EditorPreviewAsyncAPIPlugin - AsyncAPI specification rendering support
- EditorPreviewAPIDesignSystemsPlugin - API Design Systems rendering support
With a bit of adapting, we can use these plugins with SwaggerUI to provide an ability
to render AsyncAPI or API Design Systems definitions with SwaggerUI.
import SwaggerUI from 'swagger-ui';
import SwaggerUIStandalonePreset from 'swagger-ui/dist/swagger-ui-standalone-preset';
import 'swagger-editor/swagger-editor.css';
import EditorContentOriginPlugin from 'swagger-editor/plugins/editor-content-origin';
import EditorContentTypePlugin from 'swagger-editor/plugins/editor-content-type';
import EditorPreviewAsyncAPIPlugin from 'swagger-editor/plugins/editor-preview-asyncapi';
import EditorPreviewAPIDesignSystemsPlugin from 'swagger-editor/plugins/editor-preview-api-design-systems';
import SwaggerUIAdapterPlugin from 'swagger-editor/plugins/swagger-ui-adapter';SwaggerUI({
url: 'https://petstore.swagger.io/v2/swagger.json',
dom_id: '#swagger-ui',
presets: [SwaggerUI.presets.apis, SwaggerUIStandalonePreset],
plugins: [
EditorContentOriginPlugin,
EditorContentTypePlugin,
EditorPreviewAsyncAPIPlugin,
EditorPreviewAPIDesignSystemsPlugin,
SwaggerUIAdapterPlugin,
SwaggerUI.plugins.DownloadUrl,
],
});
The key here is SwaggerUIAdapter plugin which adapts SwaggerEditor plugins to use
directly with SwaggerUI.
#### Standalone mode
SwaggerUI standalone mode is supported as well. With standalone mode you'll get a TopBar with
an input where URL of the definition can be provided and this definition is subsequently loaded
by the SwaggerUI.
import SwaggerUI from 'swagger-ui';
import SwaggerUIStandalonePreset from 'swagger-ui/dist/swagger-ui-standalone-preset';
import 'swagger-ui/dist/swagger-ui.css';
import 'swagger-editor/swagger-editor.css';
import EditorContentOriginPlugin from 'swagger-editor/plugins/editor-content-origin';
import EditorContentTypePlugin from 'swagger-editor/plugins/editor-content-type';
import EditorPreviewAsyncAPIPlugin from 'swagger-editor/plugins/editor-preview-asyncapi';
import EditorPreviewAPIDesignSystemsPlugin from 'swagger-editor/plugins/editor-preview-api-design-systems';
import SwaggerUIAdapterPlugin from 'swagger-editor/plugins/swagger-ui-adapter';SwaggerUI({
url: 'https://petstore.swagger.io/v2/swagger.json',
dom_id: '#swagger-ui',
presets: [SwaggerUI.presets.apis, SwaggerUIStandalonePreset],
plugins: [
EditorContentOriginPlugin,
EditorContentTypePlugin,
EditorPreviewAsyncAPIPlugin,
EditorPreviewAPIDesignSystemsPlugin,
SwaggerUIAdapterPlugin,
SwaggerUI.plugins.DownloadUrl,
],
layout: 'StandaloneLayout',
});
#### Utilizing preview plugins via unpkg.com
It's possible to utilize preview plugins in a build-free way via unpkg.com to create a standalone
multi-spec supporting version of SwaggerUI.
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="SwaggerUIMultifold" />
<link rel="stylesheet" href="//unpkg.com/[email protected]/dist/swagger-editor.css" />
</head>
<body style="margin:0; padding:0;">
<section id="swagger-ui"></section> <script src="//unpkg.com/[email protected]/swagger-ui-bundle.js"></script>
<script src="//unpkg.com/[email protected]/swagger-ui-standalone-preset.js"></script>
<script>
ui = SwaggerUIBundle({});
// expose SwaggerUI React globally for SwaggerEditor to use
window.React = ui.React;
</script>
<script src="//unpkg.com/[email protected]/dist/umd/swagger-editor.js"></script>
<script>
SwaggerUIBundle({
url: 'https://petstore3.swagger.io/api/v3/openapi.json',
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset,
],
plugins: [
SwaggerEditor.plugins.EditorContentOrigin,
SwaggerEditor.plugins.EditorContentType,
SwaggerEditor.plugins.EditorPreviewAsyncAPI,
SwaggerEditor.plugins.EditorPreviewApiDesignSystems,
SwaggerEditor.plugins.SwaggerUIAdapter,
SwaggerUIBundle.plugins.DownloadUrl,
],
layout: 'StandaloneLayout',
});
</script>
</body>
</html>
Composing customized SwaggerEditor version
SwaggerEditor is just a number of SwaggerUI plugins used with swagger-ui-react.
Customized SwaggerEditor can be created by composing individual plugins with either swagger-ui and swagger-ui-react.
#### Plugins
List of available plugins:
- dialogs
- dropdown-menu
- dropzone
- editor-content-fixtures
- editor-content-from-file
- editor-content-origin
- editor-content-persistence
- editor-content-read-only
- editor-content-type
- editor-monaco
- editor-monaco-language-apidom
- editor-monaco-yaml-paste
- editor-preview
- editor-preview-api-design-systems
- editor-preview-asyncapi
- editor-preview-swagger-ui
- editor-safe-render
- editor-textarea
- layout
- modals
- props-change-watcher
- splash-screen
- swagger-ui-adapter
- top-bar
- versions
Individual plugins can be imported in the following way:
import EditorContentTypePlugin from 'swagger-editor/plugins/editor-content-type';
import EditorContentReadOnlyPlugin from 'swagger-editor/plugins/editor-content-read-only';#### Presets
Along with plugins, presets are available as well. Preset is a collection of plugins
that are design to work together to provide a compound feature.
List of available presets:
- textarea
- monaco
Individual presets can be imported in the following way:
import TextareaPreset from 'swagger-editor/presets/textarea';
import MonacoPreset from 'swagger-editor/presets/monaco';NOTE: Please refer to the Plug points documentation
of SwaggerUI to understand how presets are passed to SwaggerUI.
#### Composing with swagger-ui
import SwaggerUI from 'swagger-ui';
import 'swagger-ui/dist/swagger-ui.css';
import UtilPlugin from 'swagger-editor/plugins/util';
import VersionsPlugin from 'swaggereditor/plugins/versions';
import ModalsPlugin from 'swagger-editor/plugins/modals';
import DialogsPlugin from 'swagger-editor/plugins/dialogs';
import DropdownMenuPlugin from 'swagger-editor/plugins/dropdown-menu';
import DropzonePlugin from 'swagger-editor/plugins/dropzone';
import VersionsPlugin from 'swagger-editor/plugins/versions';
import EditorTextareaPlugin from 'swagger-editor/plugins/editor-textarea';
import EditorMonacoPlugin from 'swagger-editor/plugins/editor-monaco';
import EditorMonacoYamlPastePlugin from 'swagger-editor/plugins/editor-monaco-yaml-paste';
import EditorMonacoLanguageApiDOMPlugin from 'swagger-editor/plugins/editor-monaco-language-apidom';
import EditorContentReadOnlyPlugin from 'swagger-editor/plugins/editor-content-read-only';
import EditorContentOriginPlugin from 'swagger-editor/plugins/editor-content-origin';
import EditorContentTypePlugin from 'swagger-editor/plugins/editor-content-type';
import EditorContentPersistencePlugin from 'swagger-editor/plugins/editor-content-persistence';
import EditorContentFixturesPlugin from 'swagger-editor/plugins/editor-content-fixtures';
import EditorContentFromFilePlugin from 'swagger-editor/plugins/editor-content-from-file';
import EditorPreviewPlugin from 'swagger-editor/plugins/editor-preview';
import EditorPreviewSwaggerUIPlugin from 'swagger-editor/plugins/editor-preview-swagger-ui';
import EditorPreviewAsyncAPIPlugin from 'swagger-editor/plugins/editor-preview-asyncapi';
import EditorPreviewApiDesignSystemsPlugin from 'swagger-editor/plugins/editor-preview-api-design-systems';
import TopBarPlugin from 'swagger-editor/plugins/top-bar';
import SplashScreenPlugin from 'swagger-editor/plugins/splash-screen';
import LayoutPlugin from 'swagger-editor/plugins/layout';
import EditorSafeRenderPlugin from 'swagger-editor/plugins/editor-safe-render';SwaggerUI({
url: 'https://petstore.swagger.io/v2/swagger.json',
dom_id: '#swagger-editor',
plugins: [
UtilPlugin,
VersionsPlugin,
ModalsPlugin,
DialogsPlugin,
DropdownMenuPlugin,
DropzonePlugin,
VersionsPlugin,
EditorTextareaPlugin,
EditorMonacoPlugin,
EditorMonacoYamlPastePlugin,
EditorMonacoLanguageApiDOMPlugin,
EditorContentReadOnlyPlugin,
EditorContentOriginPlugin,
EditorContentTypePlugin,
EditorContentPersistencePlugin,
EditorContentFixturesPlugin,
EditorContentFromFilePlugin,
EditorPreviewPlugin,
EditorPreviewSwaggerUIPlugin,
EditorPreviewAsyncAPIPlugin,
EditorPreviewApiDesignSystemsPlugin,
TopBarPlugin,
SplashScreenPlugin,
LayoutPlugin,
EditorSafeRenderPlugin,
],
layout: 'StandaloneLayout',
});
#### Composing with swagger-ui-react
import React from 'react';
import ReactDOM from 'react-dom';
import SwaggerUI from 'swagger-ui-react';
import 'swagger-ui-react/swagger-ui.css';
import UtilPlugin from 'swagger-editor/plugins/util';
import VersionsPlugin from 'swaggereditor/plugins/versions';
import ModalsPlugin from 'swagger-editor/plugins/modals';
import DialogsPlugin from 'swagger-editor/plugins/dialogs';
import DropdownMenuPlugin from 'swagger-editor/plugins/dropdown-menu';
import DropzonePlugin from 'swagger-editor/plugins/dropzone';
import VersionsPlugin from 'swagger-editor/plugins/versions';
import EditorTextareaPlugin from 'swagger-editor/plugins/editor-textarea';
import EditorMonacoPlugin from 'swagger-editor/plugins/editor-monaco';
import EditorMonacoYamlPastePlugin from 'swagger-editor/plugins/editor-monaco-yaml-paste';
import EditorMonacoLanguageApiDOMPlugin from 'swagger-editor/plugins/editor-monaco-language-apidom';
import EditorContentReadOnlyPlugin from 'swagger-editor/plugins/editor-content-read-only';
import EditorContentOriginPlugin from 'swagger-editor/plugins/editor-content-origin';
import EditorContentTypePlugin from 'swagger-editor/plugins/editor-content-type';
import EditorContentPersistencePlugin from 'swagger-editor/plugins/editor-content-persistence';
import EditorContentFixturesPlugin from 'swagger-editor/plugins/editor-content-fixtures';
import EditorContentFromFilePlugin from 'swagger-editor/plugins/editor-content-from-file';
import EditorPreviewPlugin from 'swagger-editor/plugins/editor-preview';
import EditorPreviewSwaggerUIPlugin from 'swagger-editor/plugins/editor-preview-swagger-ui';
import EditorPreviewAsyncAPIPlugin from 'swagger-editor/plugins/editor-preview-asyncapi';
import EditorPreviewApiDesignSystemsPlugin from 'swagger-editor/plugins/editor-preview-api-design-systems';
import TopBarPlugin from 'swagger-editor/plugins/top-bar';
import SplashScreenPlugin from 'swagger-editor/plugins/splash-screen';
import LayoutPlugin from 'swagger-editor/plugins/layout';
import EditorSafeRenderPlugin from 'swagger-editor/plugins/editor-safe-render';const SwaggerEditor = () => {
return (
<SwaggerUI
url={url}
plugins={[
UtilPlugin,
VersionsPlugin,
ModalsPlugin,
DialogsPlugin,
DropdownMenuPlugin,
DropzonePlugin,
VersionsPlugin,
EditorTextareaPlugin,
EditorMonacoPlugin,
EditorMonacoYamlPastePlugin,
EditorMonacoLanguageApiDOMPlugin,
EditorContentReadOnlyPlugin,
EditorContentOriginPlugin,
EditorContentTypePlugin,
EditorContentPersistencePlugin,
EditorContentFixturesPlugin,
EditorContentFromFilePlugin,
EditorPreviewPlugin,
EditorPreviewSwaggerUIPlugin,
EditorPreviewAsyncAPIPlugin,
EditorPreviewApiDesignSystemsPlugin,
TopBarPlugin,
SplashScreenPlugin,
LayoutPlugin,
EditorSafeRenderPlugin,
]}
layout="StandaloneLayout"
/>
);
};
ReactDOM.render(<SwaggerEditor />, document.getElementById('swagger-editor'));
Docker
Pre-built DockerHub image
SwaggerEditor is available as a pre-built docker image hosted on docker.swagger.io.
$ docker pull docker.swagger.io/swaggerapi/swagger-editor:latest
$ docker run -d -p 8080:80 docker.swagger.io/swaggerapi/swagger-editor:latestBuilding locally
Privileged image:
$ npm run build:app
$ docker build . -t swaggerapi/swagger-editor:latest
$ docker run -d -p 8080:80 swaggerapi/swagger-editor:latestNow open your browser at http://localhost:8080/.
Unprivileged image:
$ npm run build:app
$ docker build . -f Dockerfile.unprivileged -t swaggerapi/swagger-editor:latest-unprivileged
$ docker run -d -p 8080:8080 swaggerapi/swagger-editor:latest-unprivilegedNow open your browser at http://localhost:8080/.
No custom environment variables are currently supported by SwaggerEditor.
License
SwaggerEditor is licensed under Apache 2.0 license.
SwaggerEditor comes with an explicit NOTICE file
containing additional legal notifications and information.
This project uses REUSE specification that defines a standardized method
for declaring copyright and licensing for software projects.
Software Bill Of Materials (SBOM)
Software Bill Of materials is available in this repository dependency graph.
Click on Export SBOM button to download the SBOM in SPDX format.