sdocs 0.0.35 → 0.0.36

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
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.36] - 2026-07-03
11
+
12
+ ### Fixed
13
+
14
+ - **Usage code is syntax-highlighted in static builds.** The usage snippet
15
+ regenerates in the browser as controls change and used to be highlighted
16
+ through a dev-server endpoint — deployed builds fell back to plain text.
17
+ Highlighting now runs client-side (lazy-loaded Shiki with the JavaScript
18
+ regex engine), identical on the dev server and any static host; the
19
+ dev-server endpoint is gone.
20
+
10
21
  ## [0.0.35] - 2026-07-03
11
22
 
12
23
  ### Fixed
@@ -0,0 +1,2 @@
1
+ /** Highlight Svelte code, matching the build-time highlighter's output shape */
2
+ export declare function highlightSvelte(code: string): Promise<string>;
@@ -0,0 +1,33 @@
1
+ // Client-side Shiki for code that's generated in the browser (the usage
2
+ // snippet re-renders as controls change). Everything loads lazily on first
3
+ // use so the Explorer bundle stays lean, and the JavaScript regex engine
4
+ // avoids shipping wasm — highlighting works the same on the dev server and
5
+ // in static builds.
6
+ let highlighterPromise;
7
+ async function getHighlighter() {
8
+ highlighterPromise ??= (async () => {
9
+ const [{ createHighlighterCore }, { createJavaScriptRegexEngine }, { bundledLanguages }, { bundledThemes }] = await Promise.all([
10
+ import('shiki/core'),
11
+ import('shiki/engine/javascript'),
12
+ import('shiki/langs'),
13
+ import('shiki/themes'),
14
+ ]);
15
+ return createHighlighterCore({
16
+ langs: [bundledLanguages.svelte],
17
+ themes: [bundledThemes['github-light'], bundledThemes['github-dark']],
18
+ engine: createJavaScriptRegexEngine({ forgiving: true }),
19
+ });
20
+ })();
21
+ return highlighterPromise;
22
+ }
23
+ /** Highlight Svelte code, matching the build-time highlighter's output shape */
24
+ export async function highlightSvelte(code) {
25
+ const highlighter = await getHighlighter();
26
+ return highlighter.codeToHtml(code, {
27
+ lang: 'svelte',
28
+ themes: {
29
+ light: 'github-light',
30
+ dark: 'github-dark',
31
+ },
32
+ });
33
+ }
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { DocEntry } from '../../types.js';
3
3
  import { Icon } from '../../ui/Icon/index.js';
4
+ import { highlightSvelte } from '../highlighter.js';
4
5
  import CollapsiblePanel from './CollapsiblePanel.svelte';
5
6
  import PreviewFrame from './PreviewFrame.svelte';
6
7
  import ControlsPanel from './ControlsPanel.svelte';
@@ -169,36 +170,20 @@
169
170
  return generateFallbackCode(componentName, propValues, cssValues);
170
171
  });
171
172
 
172
- // Highlighted usage code via server-side Shiki. The endpoint is dev-server
173
- // middleware; in a production build it doesn't exist, so after the first
174
- // failure stop asking and fall back to plain code.
173
+ // Usage code is generated in the browser, so it's highlighted client-side
174
+ // (lazy Shiki see ../highlighter.ts). Debounced while controls change;
175
+ // stale results are dropped.
175
176
  let highlightedUsageHtml = $state('');
176
177
  let highlightTimer: ReturnType<typeof setTimeout> | undefined;
177
- let highlightAvailable = true;
178
178
 
179
179
  $effect(() => {
180
180
  const code = usageCode;
181
181
  clearTimeout(highlightTimer);
182
- if (!highlightAvailable) {
183
- highlightedUsageHtml = '';
184
- return;
185
- }
186
182
  highlightTimer = setTimeout(async () => {
187
183
  try {
188
- const res = await fetch('/__sdocs/highlight', {
189
- method: 'POST',
190
- headers: { 'Content-Type': 'application/json' },
191
- body: JSON.stringify({ code, lang: 'svelte' }),
192
- });
193
- if (res.ok) {
194
- const { html } = await res.json();
195
- highlightedUsageHtml = html;
196
- } else {
197
- highlightAvailable = false;
198
- highlightedUsageHtml = '';
199
- }
184
+ const html = await highlightSvelte(code);
185
+ if (code === usageCode) highlightedUsageHtml = html;
200
186
  } catch {
201
- highlightAvailable = false;
202
187
  highlightedUsageHtml = '';
203
188
  }
204
189
  }, 150);
package/dist/vite.js CHANGED
@@ -72,25 +72,6 @@ export function sdocsPlugin(userConfig) {
72
72
  res.end('Internal error');
73
73
  });
74
74
  });
75
- // API: highlight code on demand
76
- server.middlewares.use((req, res, next) => {
77
- if (req.url !== '/__sdocs/highlight' || req.method !== 'POST')
78
- return next();
79
- let body = '';
80
- req.on('data', (chunk) => { body += chunk; });
81
- req.on('end', async () => {
82
- try {
83
- const { code, lang } = JSON.parse(body);
84
- const html = await highlight(code, lang);
85
- res.setHeader('Content-Type', 'application/json');
86
- res.end(JSON.stringify({ html }));
87
- }
88
- catch {
89
- res.statusCode = 400;
90
- res.end('Invalid request');
91
- }
92
- });
93
- });
94
75
  // Watch for .sdoc file add/unlink/change
95
76
  server.watcher.on('add', async (filePath) => {
96
77
  if (isDocFile(filePath)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdocs",
3
- "version": "0.0.35",
3
+ "version": "0.0.36",
4
4
  "description": "A lightweight documentation tool for Svelte 5 components",
5
5
  "type": "module",
6
6
  "license": "MIT",