1
1
import type { OpenAPIV3 } from 'openapi-types'
2
- import type { ApiReferenceConfiguration } from '@scalar/types'
3
2
import { ElysiaOpenAPIConfig } from '../types'
4
3
5
4
const elysiaCSS = `.light-mode {
@@ -123,10 +122,75 @@ const elysiaCSS = `.light-mode {
123
122
filter: opacity(4%) saturate(200%);
124
123
}`
125
124
125
+ const serializeArrayWithFunctions = ( arr : unknown [ ] ) : string => {
126
+ return `[${ arr . map ( ( item ) => ( typeof item === 'function' ? item . toString ( ) : JSON . stringify ( item ) ) ) . join ( ', ' ) } ]`
127
+ }
128
+
129
+ /**
130
+ * Generates the complete HTML script block required for Scalar setup, based on the provided configuration.
131
+ *
132
+ * This includes:
133
+ * 1. The Scalar bundle script.
134
+ * 2. An inline script that initializes the Scalar reference with user-provided configuration data.
135
+ *
136
+ * This function is adapted from the Scalar Core implementation.
137
+ * @see https://github.com/scalar/scalar/blob/main/packages/core/src/libs/html-rendering/html-rendering.ts#L93
138
+ *
139
+ * @param config - The Scalar configuration object.
140
+ * @returns A string containing all required <script> tags for embedding Scalar.
141
+ */
142
+ export function getScriptTags ( {
143
+ cdn,
144
+ ...configuration
145
+ } : NonNullable < ElysiaOpenAPIConfig [ 'scalar' ] > ) {
146
+ const restConfig = { ...configuration }
147
+
148
+ const functionProps : string [ ] = [ ]
149
+
150
+ for ( const [ key , value ] of Object . entries ( configuration ) as [
151
+ keyof typeof configuration ,
152
+ unknown
153
+ ] [ ] ) {
154
+ if ( typeof value === 'function' ) {
155
+ functionProps . push ( `"${ key } ": ${ value . toString ( ) } ` )
156
+ delete restConfig [ key ]
157
+ } else if (
158
+ Array . isArray ( value ) &&
159
+ value . some ( ( item ) => typeof item === 'function' )
160
+ ) {
161
+ // Handle arrays that contain functions (like plugins)
162
+ functionProps . push (
163
+ `"${ key } ": ${ serializeArrayWithFunctions ( value ) } `
164
+ )
165
+ delete restConfig [ key ]
166
+ }
167
+ }
168
+
169
+ // Stringify the rest of the configuration
170
+ const configString = JSON . stringify ( restConfig , null , 2 )
171
+ . split ( '\n' )
172
+ . map ( ( line , index ) => ( index === 0 ? line : ' ' + line ) )
173
+ . join ( '\n' )
174
+ . replace ( / \s * } $ / , '' ) // Remove the closing brace and any whitespace before it
175
+
176
+ const functionPropsString = functionProps . length
177
+ ? `,\n ${ functionProps . join ( ',\n ' ) } \n }`
178
+ : '}'
179
+
180
+ return `
181
+ <!-- Scalar script -->
182
+ <script src="${ cdn ?? 'https://cdn.jsdelivr.net/npm/@scalar/api-reference' } "></script>
183
+
184
+ <!-- Initialize the Scalar API Reference using provided config -->
185
+ <script type="text/javascript">
186
+ Scalar.createApiReference('#app', ${ configString } ${ functionPropsString } )
187
+ </script>`
188
+ }
189
+
126
190
export const ScalarRender = (
127
- info : OpenAPIV3 . InfoObject ,
128
- config : NonNullable < ElysiaOpenAPIConfig [ 'scalar' ] > ,
129
- embedSpec ?: string
191
+ info : OpenAPIV3 . InfoObject ,
192
+ config : NonNullable < ElysiaOpenAPIConfig [ 'scalar' ] > ,
193
+ embedSpec ?: string
130
194
) => `<!doctype html>
131
195
<html>
132
196
<head>
@@ -153,18 +217,7 @@ export const ScalarRender = (
153
217
</style>
154
218
</head>
155
219
<body>
156
- <script
157
- id="api-reference"
158
- data-configuration='${ JSON . stringify (
159
- Object . assign (
160
- config ,
161
- {
162
- content : embedSpec
163
- }
164
- )
165
- ) } '
166
- >
167
- </script>
168
- <script src="${ config . cdn } " crossorigin></script>
220
+ <div id="app"></div>
221
+ ${ getScriptTags ( Object . assign ( config , { content : embedSpec } ) ) }
169
222
</body>
170
223
</html>`
0 commit comments