Skip to content

Commit

Permalink
feat(useTheme): allows to customize CodeSamples langs/generator (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
enzonotario authored Nov 15, 2024
1 parent 7ff03ae commit 4636f94
Show file tree
Hide file tree
Showing 27 changed files with 1,177 additions and 269 deletions.
13 changes: 13 additions & 0 deletions dev/.vitepress/theme/components/ExampleBlock.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
</script>

<template>
<div class="flex flex-col">
<slot name="code" />

<details class="rounded border" style="padding: 24px; margin: 16px 0;" open>
<summary>Example</summary>
<slot name="example" />
</details>
</div>
</template>
4 changes: 4 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ export default defineConfigWithTheme({
text: 'Operation Badges',
link: '/customizations/operation-badges',
},
{
text: 'Code Samples',
link: '/customizations/code-samples',
},
],
},
],
Expand Down
4 changes: 4 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { theme, useOpenapi } from 'vitepress-openapi'
import DefaultTheme from 'vitepress/theme'
import spec from '../../public/openapi.json' assert {type: 'json'}
import 'vitepress-openapi/dist/style.css'
import ExampleBlock from '@dev/.vitepress/theme/components/ExampleBlock.vue'

export default {
extends: DefaultTheme,
Expand All @@ -13,5 +14,8 @@ export default {

// Use the theme.
theme.enhanceApp({ app, openapi })

// Register custom components.
app.component('ExampleBlock', ExampleBlock)
},
}
44 changes: 44 additions & 0 deletions docs/customizations/code-samples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
aside: false
outline: false
---

# Code Samples

Code Samples allow you to showcase API request examples in multiple programming languages.
This feature helps API consumers understand how to integrate with your API using their preferred language.

You can customize:

- Which languages to display (`langs`)
- Available languages for selection (`availableLangs`)
- How code is generated for each language (`generator`)


<ExampleBlock>

<template #code>

## Custom Languages

For example, you can add [Bru Markup Language](https://docs.usebruno.com/bru-lang/overview) to the list of languages to show and available languages to select from and a generator to convert the request object to Bru code.

```markdown
---
aside: false
outline: false
title: vitepress-openapi
---

<!--@include: ./parts/code-samples-example.md-->
```

</template>

<template #example>

<!--@include: ./parts/code-samples-example.md-->

</template>

</ExampleBlock>
61 changes: 14 additions & 47 deletions docs/customizations/operation-badges.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Each operation can have different badges that indicate its state, for example if

By default, only the `deprecated` badge is shown, as appropriate. You can customize the operation badges using the `useTheme({ operation: { badges: string[] })` function. **The order in which you set the badges is the order in which they will be displayed.**

For example:
<ExampleBlock>

<template #code>

```markdown
---
Expand All @@ -21,22 +23,20 @@ outline: false
title: vitepress-openapi
---

<script setup lang="ts">
import { useData } from 'vitepress'
import { useTheme } from 'vitepress-openapi'
import spec from '../public/openapi.json'
<!--@include: ./parts/operation-badges-example.md-->
```

const { isDark } = useData()
</template>

useTheme({
operation: {
badges: ['deprecated', 'operationId'],
},
})
</script>
<template #example>

<OAOperation :spec="spec" operationId="getAllArtists" :isDark="isDark" />
```
<!--@include: ./parts/operation-badges-example.md-->

</template>

</ExampleBlock>

# Custom Prefix

You can also customize the prefix of the badges by setting the `operation.badgePrefix.{badgeName}` key in the i18n messages. For example, in your `.vitepress/theme/index.ts`, before calling `theme.enhanceApp({ app })`, you can set the following:

Expand Down Expand Up @@ -67,36 +67,3 @@ export default {
theme.enhanceApp({ app })
},
}

```

## Example

---

<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import { useData } from 'vitepress'
import { useTheme } from 'vitepress-openapi'
import spec from '../public/openapi.json'

const { isDark } = useData()

onMounted(() => {
useTheme({
operation: {
badges: ['deprecated', 'operationId'],
},
})
})

onUnmounted(() => {
useTheme({
operation: {
badges: ['deprecated'],
},
})
})
</script>

<OAOperation :spec="spec" operationId="getAllArtists" :isDark="isDark" />
68 changes: 68 additions & 0 deletions docs/customizations/parts/code-samples-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script setup lang="ts">
import { useData } from 'vitepress'
import { useTheme, generateCodeSample } from 'vitepress-openapi'

const { isDark } = useData()

useTheme({
codeSamples: {
// List of languages to show in Code Samples section.
langs: [
'bruno',
...useTheme().getCodeSamplesLangs(),
],
// List of available languages to select from.
availableLanguages: [
{
lang: 'bruno',
label: 'Bruno',
highlighter: 'plaintext',
},
...useTheme().getCodeSamplesAvailableLanguages(),
],
defaultLang: 'bruno',
generator: (lang, request) => {
if (lang === 'bruno') {
return generateBruRequest(request)
}

return generateCodeSample(lang, request)
},
},
})

function generateBruRequest(request) {
const { url, method, headers, body, query } = request;

const methodLower = method.toLowerCase();

const queryString = query && Object.keys(query).length
? `${url}?${new URLSearchParams(query).toString()}`
: url;

const headersSection = headers && Object.keys(headers).length
? `headers {\n${Object.entries(headers)
.map(([key, value]) => ` ${key}: ${value}`)
.join('\n')}\n}`
: '';

const bodySection = body
? `body {\n ${JSON.stringify(body, null, 2).replace(/\n/g, '\n ')}\n}`
: '';

const bruRequest = `${methodLower} {
url: ${queryString}
}
${headersSection}
${bodySection}
`;

return bruRequest
.trim()
.replace(/\n{2,}/g, '\n\n') // Remove extra newlines
}
</script>

<OASpec :isDark="isDark" />
14 changes: 14 additions & 0 deletions docs/customizations/parts/operation-badges-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
import { useData } from 'vitepress'
import { useTheme } from 'vitepress-openapi'

const { isDark } = useData()

useTheme({
operation: {
badges: ['deprecated', 'operationId'],
},
})
</script>

<OASpec :isDark="isDark" />
14 changes: 11 additions & 3 deletions docs/layouts/custom-slots.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ outline: false

The `OAOperation` component provides several slots for customizing the operation layout.

<ExampleBlock>

<template #code>

## Description

The `description` slot allows you to customize the operation description.
Expand All @@ -21,11 +25,15 @@ title: vitepress-openapi
<!--@include: ./parts/custom-slots-example.md-->
```

This will render the following:
</template>

<template #example>

::: info Custom description slot
<!--@include: ./parts/custom-slots-example.md-->
:::

</template>

</ExampleBlock>

## WIP

3 changes: 3 additions & 0 deletions src/components/Operation/OAOperation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ function hasSlot(name) {
>
<ClientOnly>
<OATryWithVariables
:request="tryIt.request"
:operation-id="tryIt.operationId"
:path="tryIt.path"
:method="tryIt.method"
Expand All @@ -266,6 +267,7 @@ function hasSlot(name) {
:schema="tryIt.schema"
:security-schemes="tryIt.securitySchemes"
:is-dark="isDark"
@update:request="tryIt.updateRequest"
/>
</ClientOnly>
</template>
Expand Down Expand Up @@ -295,6 +297,7 @@ function hasSlot(name) {
:path="codeSamples.path"
:method="codeSamples.method"
:base-url="codeSamples.baseUrl"
:request="codeSamples.request"
:is-dark="isDark"
/>
</template>
Expand Down
28 changes: 27 additions & 1 deletion src/components/Path/OAPath.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<script setup>
import { computed, defineProps } from 'vue'
import { computed, defineProps, ref } from 'vue'
import { useTheme } from 'vitepress-openapi/composables/useTheme'
import { getOpenApiInstance } from 'vitepress-openapi'
import { buildRequest } from 'vitepress-openapi/lib/codeSamples/buildRequest'
import { propertiesTypesJsonRecursive } from 'vitepress-openapi/lib/generateSchemaJson'
const props = defineProps({
id: {
Expand Down Expand Up @@ -39,6 +41,26 @@ const operationResponses = operationParsed?.responses
const operationSlots = computed(() => themeConfig.getOperationSlots().filter(slot => !themeConfig.getOperationHiddenSlots().includes(slot)))
const operationCols = computed(() => themeConfig.getOperationCols())
const shouldBuildRequest = computed(() => ['try-it', 'code-samples'].some(slot => operationSlots.value.includes(slot)))
const request = ref(
shouldBuildRequest.value
? buildRequest({
path: operationPath,
method: operationMethod,
baseUrl,
parameters: operationParameters ?? [],
authScheme: securitySchemes.length ? securitySchemes[0] : undefined,
body: operationRequestBody ? propertiesTypesJsonRecursive(operationRequestBody, true) : undefined,
variables: {},
})
: {},
)
function updateRequest(newRequest) {
request.value = newRequest
}
</script>
<template>
Expand Down Expand Up @@ -171,6 +193,8 @@ const operationCols = computed(() => themeConfig.getOperationCols())
:parameters="operationParameters"
:schema="operationRequestBody"
:security-schemes="securitySchemes"
:request="request"
:update-request="updateRequest"
/>
</template>
Expand All @@ -182,6 +206,8 @@ const operationCols = computed(() => themeConfig.getOperationCols())
:method="operationMethod"
:base-url="baseUrl"
:path="operationPath"
:request="request"
:update-request="updateRequest"
/>
</template>
</div>
Expand Down
Loading

0 comments on commit 4636f94

Please sign in to comment.