@scalar/client-side-rendering 0.3.9 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @scalar/client-side-rendering
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#9981](https://github.com/scalar/scalar/pull/9981): Load the modern ESM build of the API Reference by default
8
+
9
+ The generated HTML now loads the code-split ESM build (`.../@scalar/api-reference/esm.js`, added in #9871) as a `<script type="module">` by default, instead of the monolithic UMD bundle. Because it is code-split, less JavaScript blocks the first render.
10
+
11
+ To keep the classic UMD bundle (loaded via `<script src>` and the `window.Scalar` global), set `cdn` to a UMD URL — for example to pin a version — or pass `bundle: false`. You can also pass `bundle: 'https://.../esm.js'` to load a specific ESM build.
12
+
13
+ When a `nonce` is set (a strict, nonce-based CSP) the UMD bundle is used automatically, because the ESM build's `import`-loaded chunks cannot be nonced. Pass `bundle: true` to force the ESM build if your CSP uses `'strict-dynamic'`.
14
+
15
+ ## 0.3.10
16
+
3
17
  ## 0.3.9
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -39,6 +39,27 @@ const html = renderApiReference({
39
39
  })
40
40
  ```
41
41
 
42
+ ### Choosing the bundle
43
+
44
+ By default the generated HTML loads the modern, code-split ESM build
45
+ (`.../@scalar/api-reference/esm.js`) as a `<script type="module">`. Because it is code-split, less
46
+ JavaScript blocks the first render.
47
+
48
+ To use the classic UMD bundle instead (loaded via `<script src>` and the `window.Scalar` global):
49
+
50
+ - `cdn: 'https://.../@scalar/api-reference'` — pin a specific UMD build.
51
+ - `bundle: false` — use the default UMD build.
52
+
53
+ You can also pass `bundle: 'https://.../esm.js'` to load a specific ESM build.
54
+
55
+ #### Content Security Policy
56
+
57
+ Passing a `nonce` implies a strict, nonce-based CSP. The ESM build loads its chunks with native
58
+ `import`, which cannot carry a nonce, so those requests would be blocked unless your policy also has
59
+ `'strict-dynamic'`. For that reason, **the UMD bundle is used automatically whenever a `nonce` is
60
+ set** — it is a single nonced `<script>` with no follow-up requests. If your CSP uses
61
+ `'strict-dynamic'` (or allow-lists the CDN host), pass `bundle: true` to opt back into the ESM build.
62
+
42
63
  ## Community
43
64
 
44
65
  We are API nerds. You too? Let's chat on Discord: <https://discord.gg/scalar>
@@ -1,7 +1,19 @@
1
1
  import type { AnyApiReferenceConfiguration, HtmlRenderingConfiguration } from '@scalar/types/api-reference';
2
2
  export type { AnyApiReferenceConfiguration, HtmlRenderingConfiguration };
3
- /** Default CDN URL for the @scalar/api-reference standalone bundle. */
3
+ /**
4
+ * Default CDN URL for the @scalar/api-reference UMD standalone bundle.
5
+ *
6
+ * This is the classic build that registers a global `window.Scalar` and is loaded through a plain
7
+ * `<script src="...">` tag. It is used when the UMD bundle is selected via `cdn` or `bundle: false`.
8
+ */
4
9
  export declare const DEFAULT_CDN = "https://cdn.jsdelivr.net/npm/@scalar/api-reference";
10
+ /**
11
+ * Default CDN URL for the @scalar/api-reference ESM standalone build (added in #9871).
12
+ *
13
+ * This is the default: it is loaded as a `<script type="module">`, so the browser only downloads the
14
+ * lazy chunks the rendered page needs instead of the whole monolithic UMD bundle.
15
+ */
16
+ export declare const DEFAULT_ESM_CDN = "https://cdn.jsdelivr.net/npm/@scalar/api-reference/esm.js";
5
17
  /**
6
18
  * Render the Scalar API Reference as a complete HTML document using the CDN.
7
19
  *
@@ -29,6 +41,17 @@ export declare function renderApiReference(options: {
29
41
  * `style="..."` attributes that a CSP nonce cannot authorize.
30
42
  */
31
43
  nonce?: string;
44
+ /**
45
+ * Which build to load. The modern, code-split ESM build is the default.
46
+ *
47
+ * Pass a URL string to load a specific ESM build, or `false` to fall back to the classic UMD
48
+ * bundle. When set, `bundle` takes precedence over both `cdn` and the `nonce` fallback.
49
+ *
50
+ * When a `nonce` is set (a strict, nonce-based CSP) the UMD bundle is used by default, because the
51
+ * ESM build's `import`-loaded chunks cannot be nonced. Pass `bundle: true` to force the ESM build
52
+ * if your CSP uses `'strict-dynamic'`.
53
+ */
54
+ bundle?: string | boolean;
32
55
  }, customTheme?: string): string;
33
56
  /**
34
57
  * Serialize a configuration object to a JavaScript object literal string.
@@ -43,12 +66,24 @@ export declare function renderApiReference(options: {
43
66
  */
44
67
  export declare function serializeConfigToJs(configuration: Record<string, unknown>): string;
45
68
  /**
46
- * The script tags to load the @scalar/api-reference package from the CDN.
69
+ * The script tag(s) to load the @scalar/api-reference package from the CDN and initialize it.
70
+ *
71
+ * By default this loads the modern, code-split ESM build as a `<script type="module">` that imports
72
+ * `createApiReference` directly (from `DEFAULT_ESM_CDN`), so the browser only downloads the lazy
73
+ * chunks the page needs rather than the whole monolithic UMD bundle before it can paint.
74
+ *
75
+ * The classic UMD bundle — loaded via `<script src>` and `Scalar.createApiReference`, exactly as
76
+ * before — is used instead when it is the safer choice or is explicitly requested: when a `nonce` is
77
+ * set (see below), when a `cdn` URL is given (for example to pin a version), or when `bundle: false`.
78
+ * Set `bundle` to `true` or a URL string to force the ESM build in those cases; `bundle` takes
79
+ * precedence over both `cdn` and the `nonce` fallback.
47
80
  *
48
- * When a `nonce` is provided it is applied to both script tags so they are allowed under a strict
49
- * `script-src` Content Security Policy.
81
+ * A `nonce` signals a strict, nonce-based `script-src` CSP. The ESM build pulls its chunks with native
82
+ * `import`, which cannot carry a nonce, so those requests are blocked under such a policy unless it
83
+ * also has `'strict-dynamic'` (or allow-lists the CDN host). The single-file UMD bundle has no such
84
+ * follow-up requests, so it is the safe default when a `nonce` is present.
50
85
  */
51
- export declare function getScriptTags(configuration: Record<string, unknown>, cdn?: string, nonce?: string): string;
86
+ export declare function getScriptTags(configuration: Record<string, unknown>, cdn?: string, nonce?: string, bundle?: string | boolean): string;
52
87
  /**
53
88
  * The configuration to pass to the @scalar/api-reference package.
54
89
  */
@@ -1 +1 @@
1
- {"version":3,"file":"html-rendering.d.ts","sourceRoot":"","sources":["../src/html-rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AAE3G,YAAY,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,CAAA;AAExE,uEAAuE;AACvE,eAAO,MAAM,WAAW,uDAAuD,CAAA;AAoE/E;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE;IACP,uCAAuC;IACvC,MAAM,EAAE,4BAA4B,CAAA;IACpC,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,EACD,WAAW,SAAK,GACf,MAAM,CA8BR;AASD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CA+BlF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAa1G;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,oBAAoB,OAAO,CAAC,0BAA0B,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAChF,MAAM,CAAC,MAAM,EAAE,OAAO,CAcxB,CAAA"}
1
+ {"version":3,"file":"html-rendering.d.ts","sourceRoot":"","sources":["../src/html-rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AAE3G,YAAY,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,CAAA;AAExE;;;;;GAKG;AACH,eAAO,MAAM,WAAW,uDAAuD,CAAA;AAE/E;;;;;GAKG;AACH,eAAO,MAAM,eAAe,8DAA8D,CAAA;AAoE1F;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE;IACP,uCAAuC;IACvC,MAAM,EAAE,4BAA4B,CAAA;IACpC,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC1B,EACD,WAAW,SAAK,GACf,MAAM,CAiCR;AASD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CA+BlF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,GAAG,CAAC,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GACxB,MAAM,CA8BR;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,oBAAoB,OAAO,CAAC,0BAA0B,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAChF,MAAM,CAAC,MAAM,EAAE,OAAO,CAcxB,CAAA"}
@@ -1,5 +1,17 @@
1
- /** Default CDN URL for the @scalar/api-reference standalone bundle. */
1
+ /**
2
+ * Default CDN URL for the @scalar/api-reference UMD standalone bundle.
3
+ *
4
+ * This is the classic build that registers a global `window.Scalar` and is loaded through a plain
5
+ * `<script src="...">` tag. It is used when the UMD bundle is selected via `cdn` or `bundle: false`.
6
+ */
2
7
  export const DEFAULT_CDN = 'https://cdn.jsdelivr.net/npm/@scalar/api-reference';
8
+ /**
9
+ * Default CDN URL for the @scalar/api-reference ESM standalone build (added in #9871).
10
+ *
11
+ * This is the default: it is loaded as a `<script type="module">`, so the browser only downloads the
12
+ * lazy chunks the rendered page needs instead of the whole monolithic UMD bundle.
13
+ */
14
+ export const DEFAULT_ESM_CDN = 'https://cdn.jsdelivr.net/npm/@scalar/api-reference/esm.js';
3
15
  /**
4
16
  * Escape HTML special characters in user-provided strings.
5
17
  */
@@ -69,7 +81,10 @@ export function renderApiReference(options, customTheme = '') {
69
81
  const { config: givenConfig, pageTitle, cdn, nonce } = options;
70
82
  const title = escapeHtml(pageTitle ?? 'Scalar API Reference');
71
83
  const unwrapped = Array.isArray(givenConfig) ? givenConfig[0] : givenConfig;
72
- const { customCss, theme, ...rest } = (unwrapped ?? {});
84
+ // `bundle` may also arrive inside the config object (integrations spread unknown options into it),
85
+ // so read it from there too and keep it out of the config that gets serialized to the client.
86
+ const { customCss, theme, bundle: configBundle, ...rest } = (unwrapped ?? {});
87
+ const bundle = (options.bundle ?? configBundle);
73
88
  const configuration = getConfiguration({
74
89
  ...rest,
75
90
  ...(theme ? { theme } : {}),
@@ -88,7 +103,7 @@ export function renderApiReference(options, customTheme = '') {
88
103
  content="width=device-width, initial-scale=1" />${cspNonceMeta}${getStyles(configuration, customTheme, nonce)}
89
104
  </head>
90
105
  <body>
91
- <div id="app"></div>${getScriptTags(configuration, cdn, nonce)}
106
+ <div id="app"></div>${getScriptTags(configuration, cdn, nonce, bundle)}
92
107
  </body>
93
108
  </html>`;
94
109
  }
@@ -137,14 +152,40 @@ export function serializeConfigToJs(configuration) {
137
152
  return `${jsonWithoutClosingBrace},\n ${functionProps.join(',\n ')}\n }`;
138
153
  }
139
154
  /**
140
- * The script tags to load the @scalar/api-reference package from the CDN.
155
+ * The script tag(s) to load the @scalar/api-reference package from the CDN and initialize it.
141
156
  *
142
- * When a `nonce` is provided it is applied to both script tags so they are allowed under a strict
143
- * `script-src` Content Security Policy.
157
+ * By default this loads the modern, code-split ESM build as a `<script type="module">` that imports
158
+ * `createApiReference` directly (from `DEFAULT_ESM_CDN`), so the browser only downloads the lazy
159
+ * chunks the page needs rather than the whole monolithic UMD bundle before it can paint.
160
+ *
161
+ * The classic UMD bundle — loaded via `<script src>` and `Scalar.createApiReference`, exactly as
162
+ * before — is used instead when it is the safer choice or is explicitly requested: when a `nonce` is
163
+ * set (see below), when a `cdn` URL is given (for example to pin a version), or when `bundle: false`.
164
+ * Set `bundle` to `true` or a URL string to force the ESM build in those cases; `bundle` takes
165
+ * precedence over both `cdn` and the `nonce` fallback.
166
+ *
167
+ * A `nonce` signals a strict, nonce-based `script-src` CSP. The ESM build pulls its chunks with native
168
+ * `import`, which cannot carry a nonce, so those requests are blocked under such a policy unless it
169
+ * also has `'strict-dynamic'` (or allow-lists the CDN host). The single-file UMD bundle has no such
170
+ * follow-up requests, so it is the safe default when a `nonce` is present.
144
171
  */
145
- export function getScriptTags(configuration, cdn, nonce) {
172
+ export function getScriptTags(configuration, cdn, nonce, bundle) {
146
173
  const configString = serializeConfigToJs(configuration);
147
174
  const nonceAttr = nonceAttribute(nonce);
175
+ // The ESM build is the default, but fall back to the single-file UMD bundle when it is the safe or
176
+ // requested choice: a `nonce` (strict nonce-based CSP, where the ESM build's `import`-loaded chunks
177
+ // cannot be nonced), a `cdn` URL, or `bundle: false`. An explicit `bundle` (true/URL) always wins.
178
+ const useUmd = bundle === false || (bundle === undefined && (cdn !== undefined || Boolean(nonce)));
179
+ if (!useUmd) {
180
+ const esmUrl = typeof bundle === 'string' ? bundle : DEFAULT_ESM_CDN;
181
+ return `
182
+ <!-- Load the Scalar API Reference -->
183
+ <script type="module"${nonceAttr}>
184
+ import { createApiReference } from '${esmUrl}'
185
+
186
+ createApiReference('#app', ${configString})
187
+ </script>`;
188
+ }
148
189
  return `
149
190
  <!-- Load the Script -->
150
191
  <script src="${cdn ?? DEFAULT_CDN}"${nonceAttr}></script>
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { type AnyApiReferenceConfiguration, DEFAULT_CDN, type HtmlRenderingConfiguration, getConfiguration, getScriptTags, renderApiReference, serializeConfigToJs, } from './html-rendering.js';
1
+ export { type AnyApiReferenceConfiguration, DEFAULT_CDN, DEFAULT_ESM_CDN, type HtmlRenderingConfiguration, getConfiguration, getScriptTags, renderApiReference, serializeConfigToJs, } from './html-rendering.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,4BAA4B,EACjC,WAAW,EACX,KAAK,0BAA0B,EAC/B,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,4BAA4B,EACjC,WAAW,EACX,eAAe,EACf,KAAK,0BAA0B,EAC/B,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAA"}
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { DEFAULT_CDN, getConfiguration, getScriptTags, renderApiReference, serializeConfigToJs, } from './html-rendering.js';
1
+ export { DEFAULT_CDN, DEFAULT_ESM_CDN, getConfiguration, getScriptTags, renderApiReference, serializeConfigToJs, } from './html-rendering.js';
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "directory": "packages/client-side-rendering"
12
12
  },
13
13
  "keywords": [],
14
- "version": "0.3.9",
14
+ "version": "0.4.0",
15
15
  "engines": {
16
16
  "node": ">=22"
17
17
  },
@@ -29,8 +29,8 @@
29
29
  "CHANGELOG.md"
30
30
  ],
31
31
  "dependencies": {
32
- "@scalar/schemas": "0.8.3",
33
- "@scalar/types": "0.18.2",
32
+ "@scalar/types": "0.19.0",
33
+ "@scalar/schemas": "0.9.0",
34
34
  "@scalar/validation": "0.6.3"
35
35
  },
36
36
  "devDependencies": {