@eventcatalog/core 4.10.15 → 4.11.0
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/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-HBVTU6ZP.js → chunk-DWC5GRFX.js} +1 -1
- package/dist/{chunk-A4JRWRCV.js → chunk-H3324DNT.js} +1 -1
- package/dist/{chunk-I6HUOX4R.js → chunk-SW7SJZIX.js} +1 -1
- package/dist/{chunk-7LWOCX4R.js → chunk-W4LDIMUI.js} +1 -1
- package/dist/{chunk-Q2ATWE7N.js → chunk-XJOKK5ZO.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +2 -1
- package/eventcatalog/public/icons/languages/android.svg +1 -0
- package/eventcatalog/public/icons/languages/angular.svg +1 -0
- package/eventcatalog/public/icons/languages/apple.svg +1 -0
- package/eventcatalog/public/icons/languages/arduino.svg +1 -0
- package/eventcatalog/public/icons/languages/bash.svg +1 -0
- package/eventcatalog/public/icons/languages/bun.svg +1 -0
- package/eventcatalog/public/icons/languages/c.svg +1 -0
- package/eventcatalog/public/icons/languages/clojure.svg +1 -0
- package/eventcatalog/public/icons/languages/cpp.svg +1 -0
- package/eventcatalog/public/icons/languages/crystal.svg +1 -0
- package/eventcatalog/public/icons/languages/csharp.svg +1 -0
- package/eventcatalog/public/icons/languages/css.svg +1 -0
- package/eventcatalog/public/icons/languages/curl.svg +1 -0
- package/eventcatalog/public/icons/languages/dart.svg +1 -0
- package/eventcatalog/public/icons/languages/deno.svg +1 -0
- package/eventcatalog/public/icons/languages/docker.svg +1 -0
- package/eventcatalog/public/icons/languages/elm.svg +1 -0
- package/eventcatalog/public/icons/languages/erlang.svg +1 -0
- package/eventcatalog/public/icons/languages/flutter.svg +1 -0
- package/eventcatalog/public/icons/languages/fortran.svg +1 -0
- package/eventcatalog/public/icons/languages/fsharp.svg +1 -0
- package/eventcatalog/public/icons/languages/git.svg +1 -0
- package/eventcatalog/public/icons/languages/gradle.svg +1 -0
- package/eventcatalog/public/icons/languages/graphql.svg +1 -0
- package/eventcatalog/public/icons/languages/grpc.svg +1 -0
- package/eventcatalog/public/icons/languages/haskell.svg +1 -0
- package/eventcatalog/public/icons/languages/hasura.svg +1 -0
- package/eventcatalog/public/icons/languages/html.svg +1 -0
- package/eventcatalog/public/icons/languages/httpie.svg +1 -0
- package/eventcatalog/public/icons/languages/javascript.svg +1 -0
- package/eventcatalog/public/icons/languages/json-schema.svg +1 -0
- package/eventcatalog/public/icons/languages/json.svg +1 -0
- package/eventcatalog/public/icons/languages/julia.svg +1 -0
- package/eventcatalog/public/icons/languages/jupyter.svg +1 -0
- package/eventcatalog/public/icons/languages/kafka.svg +1 -0
- package/eventcatalog/public/icons/languages/kotlin.svg +1 -0
- package/eventcatalog/public/icons/languages/lua.svg +1 -0
- package/eventcatalog/public/icons/languages/markdown.svg +1 -0
- package/eventcatalog/public/icons/languages/maven.svg +1 -0
- package/eventcatalog/public/icons/languages/mongodb.svg +1 -0
- package/eventcatalog/public/icons/languages/mysql.svg +1 -0
- package/eventcatalog/public/icons/languages/nginx.svg +1 -0
- package/eventcatalog/public/icons/languages/nim.svg +1 -0
- package/eventcatalog/public/icons/languages/ocaml.svg +1 -0
- package/eventcatalog/public/icons/languages/perl.svg +1 -0
- package/eventcatalog/public/icons/languages/php.svg +1 -0
- package/eventcatalog/public/icons/languages/postgresql.svg +1 -0
- package/eventcatalog/public/icons/languages/prisma.svg +1 -0
- package/eventcatalog/public/icons/languages/purescript.svg +1 -0
- package/eventcatalog/public/icons/languages/python.svg +1 -0
- package/eventcatalog/public/icons/languages/r.svg +1 -0
- package/eventcatalog/public/icons/languages/react.svg +1 -0
- package/eventcatalog/public/icons/languages/reasonml.svg +1 -0
- package/eventcatalog/public/icons/languages/redis.svg +1 -0
- package/eventcatalog/public/icons/languages/ruby.svg +1 -0
- package/eventcatalog/public/icons/languages/rust.svg +1 -0
- package/eventcatalog/public/icons/languages/sass.svg +1 -0
- package/eventcatalog/public/icons/languages/scala.svg +1 -0
- package/eventcatalog/public/icons/languages/solidity.svg +1 -0
- package/eventcatalog/public/icons/languages/spring.svg +1 -0
- package/eventcatalog/public/icons/languages/sqlite.svg +1 -0
- package/eventcatalog/public/icons/languages/svelte.svg +1 -0
- package/eventcatalog/public/icons/languages/swagger.svg +1 -0
- package/eventcatalog/public/icons/languages/swift.svg +1 -0
- package/eventcatalog/public/icons/languages/tailwind.svg +1 -0
- package/eventcatalog/public/icons/languages/terraform.svg +1 -0
- package/eventcatalog/public/icons/languages/toml.svg +1 -0
- package/eventcatalog/public/icons/languages/typescript.svg +1 -0
- package/eventcatalog/public/icons/languages/unity.svg +1 -0
- package/eventcatalog/public/icons/languages/vim.svg +1 -0
- package/eventcatalog/public/icons/languages/vue.svg +1 -0
- package/eventcatalog/public/icons/languages/webassembly.svg +1 -0
- package/eventcatalog/public/icons/languages/yaml.svg +1 -0
- package/eventcatalog/public/icons/languages/zig.svg +1 -0
- package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.astro +12 -0
- package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.tsx +176 -0
- package/eventcatalog/src/components/MDX/CodeGroup/MarkdownCodeGroup.tsx +22 -0
- package/eventcatalog/src/components/MDX/CodeGroup/code-group-icons.ts +345 -0
- package/eventcatalog/src/components/MDX/CodeGroup/code-group.css +22 -0
- package/eventcatalog/src/components/MDX/CodeGroup/code-group.spec.tsx +89 -0
- package/eventcatalog/src/components/MDX/CodeGroup/parse-panels.ts +11 -0
- package/eventcatalog/src/components/MDX/Columns/Column.astro +10 -0
- package/eventcatalog/src/components/MDX/Columns/Columns.astro +14 -0
- package/eventcatalog/src/components/MDX/Columns/MarkdownColumns.tsx +37 -0
- package/eventcatalog/src/components/MDX/Columns/columns.spec.tsx +66 -0
- package/eventcatalog/src/components/MDX/Columns/layout.ts +16 -0
- package/eventcatalog/src/components/MDX/components.tsx +6 -0
- package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +1 -1
- package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +151 -114
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +510 -248
- package/eventcatalog/src/components/SchemaExplorer/SchemaFlows.tsx +76 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaGraph.tsx +47 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaPageViewer.tsx +11 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaUsage.tsx +17 -0
- package/eventcatalog/src/components/SchemaExplorer/types.ts +48 -3
- package/eventcatalog/src/components/SchemaExplorer/utils.ts +28 -1
- package/eventcatalog/src/content.config.ts +28 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +38 -7
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +144 -37
- package/eventcatalog/src/pages/schemas/explorer/index.astro +5 -0
- package/eventcatalog/src/remark-plugins/code-group.ts +39 -0
- package/eventcatalog/src/remark-plugins/markdown-code-group.ts +59 -0
- package/eventcatalog/src/remark-plugins/markdown-columns.ts +66 -0
- package/eventcatalog/src/utils/collections/examples.ts +133 -41
- package/eventcatalog/src/utils/collections/flows.ts +43 -0
- package/eventcatalog/src/utils/collections/messages.ts +2 -2
- package/eventcatalog/src/utils/collections/schemas.ts +15 -0
- package/eventcatalog/src/utils/schema-explorer.ts +73 -7
- package/eventcatalog/src/utils/schema-usage-graph.ts +92 -0
- package/package.json +4 -4
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { buildUrl } from '@utils/url-builder';
|
|
2
|
+
|
|
3
|
+
/** Maps fence languages and file extensions to an icon in `public/icons/languages`. */
|
|
4
|
+
const LANGUAGE_ICONS: Record<string, string> = {
|
|
5
|
+
js: 'javascript',
|
|
6
|
+
javascript: 'javascript',
|
|
7
|
+
jsx: 'react',
|
|
8
|
+
mjs: 'javascript',
|
|
9
|
+
cjs: 'javascript',
|
|
10
|
+
ts: 'typescript',
|
|
11
|
+
typescript: 'typescript',
|
|
12
|
+
tsx: 'react',
|
|
13
|
+
node: 'nodejs',
|
|
14
|
+
nodejs: 'nodejs',
|
|
15
|
+
py: 'python',
|
|
16
|
+
python: 'python',
|
|
17
|
+
rb: 'ruby',
|
|
18
|
+
ruby: 'ruby',
|
|
19
|
+
php: 'php',
|
|
20
|
+
cs: 'csharp',
|
|
21
|
+
csharp: 'csharp',
|
|
22
|
+
'c#': 'csharp',
|
|
23
|
+
dotnet: 'dotnet',
|
|
24
|
+
fsharp: 'fsharp',
|
|
25
|
+
vb: 'dotnet',
|
|
26
|
+
go: 'go',
|
|
27
|
+
golang: 'go',
|
|
28
|
+
java: 'java',
|
|
29
|
+
kt: 'kotlin',
|
|
30
|
+
kotlin: 'kotlin',
|
|
31
|
+
gradle: 'gradle',
|
|
32
|
+
swift: 'swift',
|
|
33
|
+
rs: 'rust',
|
|
34
|
+
rust: 'rust',
|
|
35
|
+
c: 'c',
|
|
36
|
+
h: 'c',
|
|
37
|
+
cpp: 'cpp',
|
|
38
|
+
cc: 'cpp',
|
|
39
|
+
cxx: 'cpp',
|
|
40
|
+
hpp: 'cpp',
|
|
41
|
+
'c++': 'cpp',
|
|
42
|
+
dart: 'dart',
|
|
43
|
+
scala: 'scala',
|
|
44
|
+
r: 'r',
|
|
45
|
+
perl: 'perl',
|
|
46
|
+
pl: 'perl',
|
|
47
|
+
lua: 'lua',
|
|
48
|
+
hs: 'haskell',
|
|
49
|
+
haskell: 'haskell',
|
|
50
|
+
clj: 'clojure',
|
|
51
|
+
clojure: 'clojure',
|
|
52
|
+
erl: 'erlang',
|
|
53
|
+
erlang: 'erlang',
|
|
54
|
+
ml: 'ocaml',
|
|
55
|
+
ocaml: 'ocaml',
|
|
56
|
+
zig: 'zig',
|
|
57
|
+
deno: 'deno',
|
|
58
|
+
bun: 'bun',
|
|
59
|
+
sol: 'solidity',
|
|
60
|
+
solidity: 'solidity',
|
|
61
|
+
sh: 'bash',
|
|
62
|
+
bash: 'bash',
|
|
63
|
+
zsh: 'bash',
|
|
64
|
+
shell: 'bash',
|
|
65
|
+
console: 'bash',
|
|
66
|
+
curl: 'curl',
|
|
67
|
+
yaml: 'yaml',
|
|
68
|
+
yml: 'yaml',
|
|
69
|
+
json: 'json',
|
|
70
|
+
jsonc: 'json',
|
|
71
|
+
json5: 'json',
|
|
72
|
+
jsonschema: 'json-schema',
|
|
73
|
+
'json-schema': 'json-schema',
|
|
74
|
+
toml: 'toml',
|
|
75
|
+
md: 'markdown',
|
|
76
|
+
mdx: 'markdown',
|
|
77
|
+
markdown: 'markdown',
|
|
78
|
+
html: 'html',
|
|
79
|
+
htm: 'html',
|
|
80
|
+
vue: 'vue',
|
|
81
|
+
svelte: 'svelte',
|
|
82
|
+
angular: 'angular',
|
|
83
|
+
css: 'css',
|
|
84
|
+
scss: 'sass',
|
|
85
|
+
less: 'css',
|
|
86
|
+
graphql: 'graphql',
|
|
87
|
+
gql: 'graphql',
|
|
88
|
+
sql: 'mysql',
|
|
89
|
+
mysql: 'mysql',
|
|
90
|
+
postgres: 'postgresql',
|
|
91
|
+
postgresql: 'postgresql',
|
|
92
|
+
psql: 'postgresql',
|
|
93
|
+
docker: 'docker',
|
|
94
|
+
dockerfile: 'docker',
|
|
95
|
+
git: 'git',
|
|
96
|
+
diff: 'git',
|
|
97
|
+
hcl: 'terraform',
|
|
98
|
+
tf: 'terraform',
|
|
99
|
+
terraform: 'terraform',
|
|
100
|
+
nginx: 'nginx',
|
|
101
|
+
kafka: 'kafka',
|
|
102
|
+
'.net': 'dotnet',
|
|
103
|
+
aspnet: 'dotnet',
|
|
104
|
+
'asp.net': 'dotnet',
|
|
105
|
+
vbnet: 'dotnet',
|
|
106
|
+
'vb.net': 'dotnet',
|
|
107
|
+
fs: 'fsharp',
|
|
108
|
+
'f#': 'fsharp',
|
|
109
|
+
kts: 'kotlin',
|
|
110
|
+
groovy: 'gradle',
|
|
111
|
+
maven: 'maven',
|
|
112
|
+
pom: 'maven',
|
|
113
|
+
spring: 'spring',
|
|
114
|
+
jl: 'julia',
|
|
115
|
+
julia: 'julia',
|
|
116
|
+
f90: 'fortran',
|
|
117
|
+
fortran: 'fortran',
|
|
118
|
+
cr: 'crystal',
|
|
119
|
+
crystal: 'crystal',
|
|
120
|
+
nim: 'nim',
|
|
121
|
+
elm: 'elm',
|
|
122
|
+
purs: 'purescript',
|
|
123
|
+
purescript: 'purescript',
|
|
124
|
+
re: 'reasonml',
|
|
125
|
+
reason: 'reasonml',
|
|
126
|
+
reasonml: 'reasonml',
|
|
127
|
+
flutter: 'flutter',
|
|
128
|
+
grpc: 'grpc',
|
|
129
|
+
sqlite: 'sqlite',
|
|
130
|
+
mongodb: 'mongodb',
|
|
131
|
+
mongo: 'mongodb',
|
|
132
|
+
redis: 'redis',
|
|
133
|
+
prisma: 'prisma',
|
|
134
|
+
wasm: 'webassembly',
|
|
135
|
+
wat: 'webassembly',
|
|
136
|
+
webassembly: 'webassembly',
|
|
137
|
+
sass: 'sass',
|
|
138
|
+
tailwind: 'tailwind',
|
|
139
|
+
tailwindcss: 'tailwind',
|
|
140
|
+
http: 'httpie',
|
|
141
|
+
httpie: 'httpie',
|
|
142
|
+
swagger: 'swagger',
|
|
143
|
+
ipynb: 'jupyter',
|
|
144
|
+
jupyter: 'jupyter',
|
|
145
|
+
android: 'android',
|
|
146
|
+
ios: 'apple',
|
|
147
|
+
objc: 'apple',
|
|
148
|
+
objectivec: 'apple',
|
|
149
|
+
'objective-c': 'apple',
|
|
150
|
+
ino: 'arduino',
|
|
151
|
+
arduino: 'arduino',
|
|
152
|
+
unity: 'unity',
|
|
153
|
+
vim: 'vim',
|
|
154
|
+
hasura: 'hasura',
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/** Languages that have a display name but no icon. */
|
|
158
|
+
const NAME_ONLY_LANGUAGES: Record<string, string> = {
|
|
159
|
+
xml: 'XML',
|
|
160
|
+
xsd: 'XML Schema',
|
|
161
|
+
txt: 'Text',
|
|
162
|
+
text: 'Text',
|
|
163
|
+
plaintext: 'Text',
|
|
164
|
+
plain: 'Text',
|
|
165
|
+
ini: 'INI',
|
|
166
|
+
properties: 'Properties',
|
|
167
|
+
env: 'Env',
|
|
168
|
+
dotenv: 'Env',
|
|
169
|
+
makefile: 'Makefile',
|
|
170
|
+
make: 'Makefile',
|
|
171
|
+
cmake: 'CMake',
|
|
172
|
+
tex: 'LaTeX',
|
|
173
|
+
latex: 'LaTeX',
|
|
174
|
+
csv: 'CSV',
|
|
175
|
+
groovy: 'Groovy',
|
|
176
|
+
elixir: 'Elixir',
|
|
177
|
+
ex: 'Elixir',
|
|
178
|
+
exs: 'Elixir',
|
|
179
|
+
matlab: 'MATLAB',
|
|
180
|
+
ps1: 'PowerShell',
|
|
181
|
+
powershell: 'PowerShell',
|
|
182
|
+
pwsh: 'PowerShell',
|
|
183
|
+
bat: 'Batch',
|
|
184
|
+
cmd: 'Batch',
|
|
185
|
+
regex: 'Regex',
|
|
186
|
+
mermaid: 'Mermaid',
|
|
187
|
+
hbs: 'Handlebars',
|
|
188
|
+
handlebars: 'Handlebars',
|
|
189
|
+
ejs: 'EJS',
|
|
190
|
+
pug: 'Pug',
|
|
191
|
+
nix: 'Nix',
|
|
192
|
+
cobol: 'COBOL',
|
|
193
|
+
scheme: 'Scheme',
|
|
194
|
+
racket: 'Racket',
|
|
195
|
+
lisp: 'Lisp',
|
|
196
|
+
ada: 'Ada',
|
|
197
|
+
pascal: 'Pascal',
|
|
198
|
+
prolog: 'Prolog',
|
|
199
|
+
smalltalk: 'Smalltalk',
|
|
200
|
+
vhdl: 'VHDL',
|
|
201
|
+
verilog: 'Verilog',
|
|
202
|
+
asm: 'Assembly',
|
|
203
|
+
assembly: 'Assembly',
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/** Formats that already have an icon elsewhere under `public/icons`. */
|
|
207
|
+
const SCHEMA_ICONS: Record<string, string> = {
|
|
208
|
+
avro: 'avro',
|
|
209
|
+
avsc: 'avro',
|
|
210
|
+
proto: 'proto',
|
|
211
|
+
protobuf: 'proto',
|
|
212
|
+
openapi: 'openapi',
|
|
213
|
+
asyncapi: 'asyncapi',
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const LANGUAGE_NAMES: Record<string, string> = {
|
|
217
|
+
javascript: 'JavaScript',
|
|
218
|
+
typescript: 'TypeScript',
|
|
219
|
+
react: 'React',
|
|
220
|
+
nodejs: 'Node.js',
|
|
221
|
+
python: 'Python',
|
|
222
|
+
ruby: 'Ruby',
|
|
223
|
+
php: 'PHP',
|
|
224
|
+
csharp: 'C#',
|
|
225
|
+
dotnet: '.NET',
|
|
226
|
+
go: 'Go',
|
|
227
|
+
java: 'Java',
|
|
228
|
+
kotlin: 'Kotlin',
|
|
229
|
+
gradle: 'Gradle',
|
|
230
|
+
swift: 'Swift',
|
|
231
|
+
rust: 'Rust',
|
|
232
|
+
c: 'C',
|
|
233
|
+
cpp: 'C++',
|
|
234
|
+
dart: 'Dart',
|
|
235
|
+
scala: 'Scala',
|
|
236
|
+
r: 'R',
|
|
237
|
+
perl: 'Perl',
|
|
238
|
+
lua: 'Lua',
|
|
239
|
+
haskell: 'Haskell',
|
|
240
|
+
clojure: 'Clojure',
|
|
241
|
+
erlang: 'Erlang',
|
|
242
|
+
ocaml: 'OCaml',
|
|
243
|
+
zig: 'Zig',
|
|
244
|
+
deno: 'Deno',
|
|
245
|
+
bun: 'Bun',
|
|
246
|
+
solidity: 'Solidity',
|
|
247
|
+
bash: 'Shell',
|
|
248
|
+
curl: 'cURL',
|
|
249
|
+
yaml: 'YAML',
|
|
250
|
+
json: 'JSON',
|
|
251
|
+
'json-schema': 'JSON Schema',
|
|
252
|
+
toml: 'TOML',
|
|
253
|
+
markdown: 'Markdown',
|
|
254
|
+
html: 'HTML',
|
|
255
|
+
vue: 'Vue',
|
|
256
|
+
svelte: 'Svelte',
|
|
257
|
+
angular: 'Angular',
|
|
258
|
+
css: 'CSS',
|
|
259
|
+
graphql: 'GraphQL',
|
|
260
|
+
mysql: 'SQL',
|
|
261
|
+
postgresql: 'PostgreSQL',
|
|
262
|
+
docker: 'Docker',
|
|
263
|
+
git: 'Git',
|
|
264
|
+
terraform: 'Terraform',
|
|
265
|
+
nginx: 'NGINX',
|
|
266
|
+
kafka: 'Kafka',
|
|
267
|
+
avro: 'Avro',
|
|
268
|
+
proto: 'Protobuf',
|
|
269
|
+
openapi: 'OpenAPI',
|
|
270
|
+
asyncapi: 'AsyncAPI',
|
|
271
|
+
fsharp: 'F#',
|
|
272
|
+
maven: 'Maven',
|
|
273
|
+
spring: 'Spring',
|
|
274
|
+
julia: 'Julia',
|
|
275
|
+
fortran: 'Fortran',
|
|
276
|
+
crystal: 'Crystal',
|
|
277
|
+
nim: 'Nim',
|
|
278
|
+
elm: 'Elm',
|
|
279
|
+
purescript: 'PureScript',
|
|
280
|
+
reasonml: 'ReasonML',
|
|
281
|
+
flutter: 'Flutter',
|
|
282
|
+
grpc: 'gRPC',
|
|
283
|
+
sqlite: 'SQLite',
|
|
284
|
+
mongodb: 'MongoDB',
|
|
285
|
+
redis: 'Redis',
|
|
286
|
+
prisma: 'Prisma',
|
|
287
|
+
webassembly: 'WebAssembly',
|
|
288
|
+
sass: 'Sass',
|
|
289
|
+
tailwind: 'Tailwind CSS',
|
|
290
|
+
httpie: 'HTTP',
|
|
291
|
+
swagger: 'Swagger',
|
|
292
|
+
jupyter: 'Jupyter',
|
|
293
|
+
android: 'Android',
|
|
294
|
+
apple: 'Apple',
|
|
295
|
+
arduino: 'Arduino',
|
|
296
|
+
unity: 'Unity',
|
|
297
|
+
vim: 'Vim',
|
|
298
|
+
hasura: 'Hasura',
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const normalize = (value: string) => value.trim().toLowerCase();
|
|
302
|
+
|
|
303
|
+
const getExtension = (label?: string) => {
|
|
304
|
+
const match = label?.trim().match(/\.([a-z0-9+#]+)$/i);
|
|
305
|
+
return match?.[1];
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const resolveIconKey = (language?: string, label?: string) => {
|
|
309
|
+
for (const candidate of [language, getExtension(label)]) {
|
|
310
|
+
if (!candidate) continue;
|
|
311
|
+
const key = normalize(candidate);
|
|
312
|
+
if (LANGUAGE_ICONS[key]) return LANGUAGE_ICONS[key];
|
|
313
|
+
if (SCHEMA_ICONS[key]) return SCHEMA_ICONS[key];
|
|
314
|
+
}
|
|
315
|
+
return undefined;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Human readable language name for a code panel, for example `Python` for `py`.
|
|
320
|
+
* Falls back to the fence language as written, or the label when there is no language.
|
|
321
|
+
*/
|
|
322
|
+
export function getCodeLanguageName(language?: string, label?: string): string {
|
|
323
|
+
const key = resolveIconKey(language, label);
|
|
324
|
+
if (key && LANGUAGE_NAMES[key]) return LANGUAGE_NAMES[key];
|
|
325
|
+
for (const candidate of [language, getExtension(label)]) {
|
|
326
|
+
if (candidate && NAME_ONLY_LANGUAGES[normalize(candidate)]) return NAME_ONLY_LANGUAGES[normalize(candidate)];
|
|
327
|
+
}
|
|
328
|
+
const raw = language?.trim();
|
|
329
|
+
if (raw) return raw.charAt(0).toUpperCase() + raw.slice(1);
|
|
330
|
+
return label ?? '';
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Resolves an icon URL for a code panel from its fence language, falling back to the file
|
|
335
|
+
* extension in the panel label. Returns `undefined` when there is no matching icon.
|
|
336
|
+
*/
|
|
337
|
+
export function getCodeLanguageIconUrl(language?: string, label?: string): string | undefined {
|
|
338
|
+
for (const candidate of [language, getExtension(label)]) {
|
|
339
|
+
if (!candidate) continue;
|
|
340
|
+
const key = normalize(candidate);
|
|
341
|
+
if (LANGUAGE_ICONS[key]) return buildUrl(`/icons/languages/${LANGUAGE_ICONS[key]}.svg`, true);
|
|
342
|
+
if (SCHEMA_ICONS[key]) return buildUrl(`/icons/${SCHEMA_ICONS[key]}.svg`, true);
|
|
343
|
+
}
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* Overrides for markup generated by syntax highlighters, outside React's control. */
|
|
2
|
+
.ec-code-group [data-example-code-block] {
|
|
3
|
+
margin: 0;
|
|
4
|
+
border: 0;
|
|
5
|
+
border-radius: 0;
|
|
6
|
+
box-shadow: none;
|
|
7
|
+
}
|
|
8
|
+
.ec-code-group [data-code-panel] .expressive-code {
|
|
9
|
+
margin: 0;
|
|
10
|
+
}
|
|
11
|
+
.ec-code-group [data-code-panel] .frame {
|
|
12
|
+
border: 0;
|
|
13
|
+
border-radius: 0;
|
|
14
|
+
box-shadow: none;
|
|
15
|
+
}
|
|
16
|
+
.ec-code-group [data-code-panel] .frame > .header,
|
|
17
|
+
.ec-code-group [data-code-panel] .frame .copy {
|
|
18
|
+
display: none;
|
|
19
|
+
}
|
|
20
|
+
.ec-code-group [data-code-panel] pre {
|
|
21
|
+
border-radius: 0;
|
|
22
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { act } from 'react';
|
|
4
|
+
import { createRoot, type Root } from 'react-dom/client';
|
|
5
|
+
import CodeGroup from './CodeGroup';
|
|
6
|
+
import { MarkdownCodeGroup } from './MarkdownCodeGroup';
|
|
7
|
+
import { parseCodePanels } from './parse-panels';
|
|
8
|
+
import { getCodeLanguageIconUrl, getCodeLanguageName } from './code-group-icons';
|
|
9
|
+
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
|
10
|
+
const roots: Root[] = [];
|
|
11
|
+
function render(element: React.ReactNode) {
|
|
12
|
+
const container = document.createElement('div');
|
|
13
|
+
document.body.append(container);
|
|
14
|
+
const root = createRoot(container);
|
|
15
|
+
roots.push(root);
|
|
16
|
+
act(() => root.render(element));
|
|
17
|
+
return container;
|
|
18
|
+
}
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
act(() => roots.splice(0).forEach((root) => root.unmount()));
|
|
21
|
+
document.body.innerHTML = '';
|
|
22
|
+
});
|
|
23
|
+
const items = [
|
|
24
|
+
{ label: 'hello.js', language: 'js', content: <pre>first</pre> },
|
|
25
|
+
{ label: 'hello.py', language: 'python', content: <pre>second</pre> },
|
|
26
|
+
];
|
|
27
|
+
describe('React CodeGroup', () => {
|
|
28
|
+
it('switches panels with keyboard focus and synchronizes matching groups', () => {
|
|
29
|
+
const a = render(<CodeGroup items={items} />);
|
|
30
|
+
const b = render(<CodeGroup items={items} dropdown />);
|
|
31
|
+
const tabs = a.querySelectorAll<HTMLButtonElement>('[role=tab]');
|
|
32
|
+
act(() => tabs[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })));
|
|
33
|
+
expect(tabs[1].getAttribute('aria-selected')).toBe('true');
|
|
34
|
+
expect(document.activeElement).toBe(tabs[1]);
|
|
35
|
+
expect(a.querySelectorAll<HTMLElement>('[role=tabpanel]')[0].hidden).toBe(true);
|
|
36
|
+
expect(b.querySelectorAll<HTMLElement>('[role=region]')[1].hidden).toBe(false);
|
|
37
|
+
expect(b.querySelector('button')?.textContent).toBe('Python');
|
|
38
|
+
});
|
|
39
|
+
it('copies the selected code', async () => {
|
|
40
|
+
const writeText = vi.fn().mockResolvedValue(undefined);
|
|
41
|
+
Object.assign(navigator, { clipboard: { writeText } });
|
|
42
|
+
const root = render(<CodeGroup items={items} />);
|
|
43
|
+
act(() => root.querySelectorAll<HTMLButtonElement>('[role=tab]')[1].click());
|
|
44
|
+
await act(async () => root.querySelector<HTMLButtonElement>('[aria-label="Copy code"]')!.click());
|
|
45
|
+
expect(writeText).toHaveBeenCalledWith('second');
|
|
46
|
+
expect(root.querySelector('[aria-label="Copied!"]')).not.toBeNull();
|
|
47
|
+
});
|
|
48
|
+
it('preserves highlighted Astro HTML and decodes copy newlines', async () => {
|
|
49
|
+
const parsed = parseCodePanels(
|
|
50
|
+
'<div data-code-panel="file.js" data-code-lang="js"><pre><code><span>one</span>two</code></pre><button data-code="onetwo"></button></div>'
|
|
51
|
+
);
|
|
52
|
+
const writeText = vi.fn().mockResolvedValue(undefined);
|
|
53
|
+
Object.assign(navigator, { clipboard: { writeText } });
|
|
54
|
+
const root = render(<CodeGroup items={parsed} />);
|
|
55
|
+
expect(root.querySelector('code span')?.textContent).toBe('one');
|
|
56
|
+
await act(async () => root.querySelector<HTMLButtonElement>('[aria-label="Copy code"]')!.click());
|
|
57
|
+
expect(writeText).toHaveBeenCalledWith('one\ntwo');
|
|
58
|
+
});
|
|
59
|
+
it('accepts Markdown panels and escapes labels', () => {
|
|
60
|
+
const root = render(
|
|
61
|
+
<MarkdownCodeGroup>
|
|
62
|
+
<div data-code-panel="<script>" data-code-lang="js">
|
|
63
|
+
<pre>hello</pre>
|
|
64
|
+
</div>
|
|
65
|
+
</MarkdownCodeGroup>
|
|
66
|
+
);
|
|
67
|
+
expect(root.querySelector('[role=tab]')?.textContent).toBe('<script>');
|
|
68
|
+
expect(root.querySelector('script')).toBeNull();
|
|
69
|
+
expect(root.querySelector('pre')?.textContent).toBe('hello');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('language names and icons', () => {
|
|
74
|
+
it.each([
|
|
75
|
+
['cs', undefined, 'C#', '/icons/languages/csharp.svg'],
|
|
76
|
+
['dotnet', undefined, '.NET', '/icons/languages/dotnet.svg'],
|
|
77
|
+
['fsharp', undefined, 'F#', '/icons/languages/fsharp.svg'],
|
|
78
|
+
['java', undefined, 'Java', '/icons/languages/java.svg'],
|
|
79
|
+
['kts', undefined, 'Kotlin', '/icons/languages/kotlin.svg'],
|
|
80
|
+
[undefined, 'Main.go', 'Go', '/icons/languages/go.svg'],
|
|
81
|
+
['powershell', undefined, 'PowerShell', undefined],
|
|
82
|
+
['xml', undefined, 'XML', undefined],
|
|
83
|
+
['avsc', undefined, 'Avro', '/icons/avro.svg'],
|
|
84
|
+
['brainfuck', undefined, 'Brainfuck', undefined],
|
|
85
|
+
])('maps %s / %s to %s', (language, label, name, icon) => {
|
|
86
|
+
expect(getCodeLanguageName(language, label)).toBe(name);
|
|
87
|
+
expect(getCodeLanguageIconUrl(language, label)).toBe(icon);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { parseFragment, serialize } from 'parse5';
|
|
2
|
+
|
|
3
|
+
// Only parse the trusted HTML produced by Astro's rendered MDX slot.
|
|
4
|
+
export function parseCodePanels(html: string) {
|
|
5
|
+
return parseFragment(html).childNodes.flatMap((node) => {
|
|
6
|
+
if (!('attrs' in node)) return [];
|
|
7
|
+
const label = node.attrs.find((attr) => attr.name === 'data-code-panel')?.value;
|
|
8
|
+
if (label === undefined) return [];
|
|
9
|
+
return [{ label, language: node.attrs.find((attr) => attr.name === 'data-code-lang')?.value, html: serialize(node) }];
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { columnClass, stickyClass } from './layout';
|
|
3
|
+
interface Props {
|
|
4
|
+
sticky?: boolean;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
const { sticky = false, className } = Astro.props;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<div class:list={[columnClass, sticky && stickyClass, className]} data-sticky={String(sticky)}><slot /></div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getColumnsLayout, columnsClass, gridClass } from './layout';
|
|
3
|
+
interface Props {
|
|
4
|
+
cols?: number;
|
|
5
|
+
ratio?: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
const { cols = 2, ratio, className } = Astro.props;
|
|
9
|
+
const { template } = getColumnsLayout(cols, ratio);
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<div class:list={[columnsClass, className]} style={{ '--ec-columns-template': template }}>
|
|
13
|
+
<div class={gridClass}><slot /></div>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import { getColumnsLayout, columnsClass, gridClass, columnClass, stickyClass } from './layout';
|
|
3
|
+
|
|
4
|
+
export function MarkdownColumns({
|
|
5
|
+
children,
|
|
6
|
+
cols = 2,
|
|
7
|
+
ratio,
|
|
8
|
+
className = '',
|
|
9
|
+
}: {
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
cols?: number;
|
|
12
|
+
ratio?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}) {
|
|
15
|
+
const { template } = getColumnsLayout(cols, ratio);
|
|
16
|
+
return (
|
|
17
|
+
<div className={`${columnsClass} ${className}`} style={{ '--ec-columns-template': template } as CSSProperties}>
|
|
18
|
+
<div className={gridClass}>{children}</div>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function MarkdownColumn({
|
|
24
|
+
children,
|
|
25
|
+
sticky = false,
|
|
26
|
+
className = '',
|
|
27
|
+
}: {
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
sticky?: boolean;
|
|
30
|
+
className?: string;
|
|
31
|
+
}) {
|
|
32
|
+
return (
|
|
33
|
+
<div className={`${columnClass} ${sticky ? stickyClass : ''} ${className}`} data-sticky={String(sticky)}>
|
|
34
|
+
{children}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
3
|
+
import ReactMarkdown from 'react-markdown';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import * as runtime from 'react/jsx-runtime';
|
|
7
|
+
import { getColumnsLayout } from './layout';
|
|
8
|
+
import { MarkdownColumns, MarkdownColumn } from './MarkdownColumns';
|
|
9
|
+
import { remarkMarkdownColumns } from '../../../remark-plugins/markdown-columns';
|
|
10
|
+
import { remarkMarkdownCodeGroup } from '../../../remark-plugins/markdown-code-group';
|
|
11
|
+
|
|
12
|
+
const source =
|
|
13
|
+
'<Columns cols={2} ratio="2:1">\n <Column>\n\n## Publish an order\n\nSome **explanation**.\n\n </Column>\n <Column sticky>\n\n<CodeGroup>\n\n```json OrderCreated\n{"id":"123"}\n```\n\n```js JavaScript\npublish();\n```\n\n</CodeGroup>\n\n </Column>\n</Columns>';
|
|
14
|
+
|
|
15
|
+
describe('Columns', () => {
|
|
16
|
+
it('defaults to two columns and supports one through four', () => {
|
|
17
|
+
expect(getColumnsLayout().template).toBe('repeat(2, minmax(0, 1fr))');
|
|
18
|
+
for (const count of [1, 2, 3, 4]) expect(getColumnsLayout(count).count).toBe(count);
|
|
19
|
+
});
|
|
20
|
+
it('uses valid ratios and safely falls back for invalid options', () => {
|
|
21
|
+
expect(getColumnsLayout(2, '2:1').template).toBe('minmax(0, 2fr) minmax(0, 1fr)');
|
|
22
|
+
for (const ratio of ['0:1', '-1:2', 'NaN:1', '1:2:3', '1fr:2fr', 'Infinity:1']) {
|
|
23
|
+
expect(getColumnsLayout(2, ratio).template).toBe('repeat(2, minmax(0, 1fr))');
|
|
24
|
+
}
|
|
25
|
+
for (const count of [0, 5, 1.5, NaN]) expect(getColumnsLayout(count).count).toBe(2);
|
|
26
|
+
});
|
|
27
|
+
it('renders the same API as real MDX with rich content and nested components', async () => {
|
|
28
|
+
const require = createRequire(import.meta.url);
|
|
29
|
+
const mdxRequire = createRequire(require.resolve('@astrojs/mdx'));
|
|
30
|
+
const { evaluate } = await import(pathToFileURL(mdxRequire.resolve('@mdx-js/mdx')).href);
|
|
31
|
+
const result = await evaluate(source, runtime);
|
|
32
|
+
const html = renderToStaticMarkup(
|
|
33
|
+
runtime.jsx(result.default, {
|
|
34
|
+
components: {
|
|
35
|
+
Columns: MarkdownColumns,
|
|
36
|
+
Column: MarkdownColumn,
|
|
37
|
+
CodeGroup: 'section',
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
expect(html).toContain('--ec-columns-template:minmax(0, 2fr) minmax(0, 1fr)');
|
|
42
|
+
expect(html).toContain('data-sticky="true"');
|
|
43
|
+
expect(html).toContain('<strong>explanation</strong>');
|
|
44
|
+
expect(html).toContain('language-json');
|
|
45
|
+
});
|
|
46
|
+
it('recognizes adjacent layout tags in Markdown and lets CodeGroup transform within columns', () => {
|
|
47
|
+
const html = renderToStaticMarkup(
|
|
48
|
+
<ReactMarkdown remarkPlugins={[remarkMarkdownColumns, remarkMarkdownCodeGroup]}>{source}</ReactMarkdown>
|
|
49
|
+
);
|
|
50
|
+
expect(html).toContain('data-columns="true"');
|
|
51
|
+
expect(html.match(/data-column="true"/g)).toHaveLength(2);
|
|
52
|
+
expect(html).toContain('data-cols="2"');
|
|
53
|
+
expect(html).toContain('data-ratio="2:1"');
|
|
54
|
+
expect(html).toContain('data-sticky="true"');
|
|
55
|
+
expect(html).toContain('data-code-panel="OrderCreated"');
|
|
56
|
+
expect(html).toContain('<strong>explanation</strong>');
|
|
57
|
+
expect(html).not.toContain('<Column');
|
|
58
|
+
});
|
|
59
|
+
it('preserves nested column layouts and leaves unrelated HTML escaped', () => {
|
|
60
|
+
const nested =
|
|
61
|
+
'<Columns>\n\n<Column>\n\n<Columns cols={1}>\n\n<Column>\n\nInner\n\n</Column>\n\n</Columns>\n\n</Column>\n\n</Columns>\n\n<script>alert(1)</script>';
|
|
62
|
+
const html = renderToStaticMarkup(<ReactMarkdown remarkPlugins={[remarkMarkdownColumns]}>{nested}</ReactMarkdown>);
|
|
63
|
+
expect(html.match(/data-columns="true"/g)).toHaveLength(2);
|
|
64
|
+
expect(html).not.toContain('<script>');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function getColumnsLayout(cols: number = 2, ratio?: string) {
|
|
2
|
+
const count = Number.isInteger(cols) && cols >= 1 && cols <= 4 ? cols : 2;
|
|
3
|
+
const weights = ratio?.split(':').map(Number);
|
|
4
|
+
const validRatio = weights?.length === count && weights.every((weight) => Number.isFinite(weight) && weight > 0);
|
|
5
|
+
return {
|
|
6
|
+
count,
|
|
7
|
+
template: validRatio ? weights.map((weight) => `minmax(0, ${weight}fr)`).join(' ') : `repeat(${count}, minmax(0, 1fr))`,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const columnsClass = 'ec-columns @container/ec-columns my-6';
|
|
12
|
+
export const gridClass =
|
|
13
|
+
'ec-columns-grid grid grid-cols-1 items-start gap-8 [&>*]:min-w-0 @min-[640px]/ec-columns:grid-cols-[var(--ec-columns-template)]';
|
|
14
|
+
export const columnClass = 'ec-column min-w-0 [&>:first-child]:mt-0 [&>:last-child]:mb-0';
|
|
15
|
+
export const stickyClass =
|
|
16
|
+
'@min-[640px]/ec-columns:sticky @min-[640px]/ec-columns:top-[var(--ec-column-sticky-top,5rem)] print:static';
|
|
@@ -22,6 +22,9 @@ import ResourceGroupTable from '@components/MDX/ResourceGroupTable/ResourceGroup
|
|
|
22
22
|
import EntityPropertiesTable from '@components/MDX/EntityPropertiesTable/EntityPropertiesTable.astro';
|
|
23
23
|
import Tabs from '@components/MDX/Tabs/Tabs.astro';
|
|
24
24
|
import TabItem from '@components/MDX/Tabs/TabItem.astro';
|
|
25
|
+
import CodeGroup from '@components/MDX/CodeGroup/CodeGroup.astro';
|
|
26
|
+
import Columns from '@components/MDX/Columns/Columns.astro';
|
|
27
|
+
import Column from '@components/MDX/Columns/Column.astro';
|
|
25
28
|
import ResourceLink from '@components/MDX/ResourceLink/ResourceLink.astro';
|
|
26
29
|
import ResourceRef from '@components/MDX/ResourceRef/ResourceRef.astro';
|
|
27
30
|
import Link from '@components/MDX/Link/Link.astro';
|
|
@@ -85,6 +88,9 @@ const components = (props: any) => {
|
|
|
85
88
|
Step,
|
|
86
89
|
Steps,
|
|
87
90
|
TabItem,
|
|
91
|
+
CodeGroup,
|
|
92
|
+
Columns,
|
|
93
|
+
Column,
|
|
88
94
|
Tabs,
|
|
89
95
|
Tile,
|
|
90
96
|
Tiles,
|
|
@@ -134,7 +134,7 @@ export default function ApiContentViewer({ message, onCopy, copiedId, apiAccessE
|
|
|
134
134
|
className="rounded-md border bg-[rgb(var(--ec-content-hover)/0.45)] px-2.5 py-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))]"
|
|
135
135
|
style={{ borderColor: boxBorderColor }}
|
|
136
136
|
>
|
|
137
|
-
|
|
137
|
+
text/plain
|
|
138
138
|
</code>
|
|
139
139
|
</div>
|
|
140
140
|
<div className="flex items-center justify-between gap-4 border-t px-4 py-3" style={{ borderColor: dividerColor }}>
|