sdocs 0.0.33 → 0.0.34

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,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.34] - 2026-07-03
11
+
12
+ ### Fixed
13
+
14
+ - **Preview paths are short and project-relative.** Preview URLs and emitted
15
+ file names used to encode the doc's absolute filesystem path, which leaked
16
+ the machine's directory layout into published sites and produced path
17
+ segments long enough for GitHub Pages to reject the deployment. Paths are
18
+ now encoded relative to the project root.
19
+
10
20
  ## [0.0.33] - 2026-07-03
11
21
 
12
22
  ### Added
@@ -5,7 +5,7 @@ import { dirname, resolve, join } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { discoverDocFiles, getSdocKind } from './discovery.js';
7
7
  import { extractSnippets, hasDefaultSnippet } from './snippet-extractor.js';
8
- import { base64urlEncode } from './snippet-compiler.js';
8
+ import { encodeDocPath, setDocPathRoot } from './snippet-compiler.js';
9
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
10
  /** Source Explorer directory in the installed package (dist/explorer, next to dist/server) */
11
11
  function getExplorerSourceDir() {
@@ -174,6 +174,9 @@ export async function generateDevFiles(config, cwd) {
174
174
  /** Generate the staging directory with entry + preview HTML files for build mode */
175
175
  export async function generateBuildFiles(config, cwd) {
176
176
  const sdocsDir = await createStagingDir(cwd);
177
+ // The staging dir becomes the Vite root; encode doc paths against it now so
178
+ // these inputs match the URLs the plugin generates later.
179
+ setDocPathRoot(sdocsDir);
177
180
  // Copy Explorer components into the staging dir
178
181
  await copyExplorerApp(sdocsDir);
179
182
  await writeFile(resolve(sdocsDir, 'index.html'), generateIndexHtml(config.logo));
@@ -184,7 +187,7 @@ export async function generateBuildFiles(config, cwd) {
184
187
  // Discover snippets and generate preview HTML pages
185
188
  const docSnippets = await discoverSnippets(config, cwd);
186
189
  for (const { filePath, snippetNames } of docSnippets) {
187
- const encoded = base64urlEncode(filePath);
190
+ const encoded = encodeDocPath(filePath);
188
191
  for (const snippetName of snippetNames) {
189
192
  const iframeId = `/@sdocs/iframe/${encoded}/${snippetName}.svelte`;
190
193
  const previewDir = resolve(sdocsDir, 'previews', encoded);
@@ -2,6 +2,10 @@
2
2
  export declare function base64urlEncode(str: string): string;
3
3
  /** Base64url decode */
4
4
  export declare function base64urlDecode(str: string): string;
5
+ /** Set the root that doc paths are encoded against (the Vite root / staging dir) */
6
+ export declare function setDocPathRoot(root: string): void;
7
+ /** Encode a doc file path for use in URLs and emitted file names */
8
+ export declare function encodeDocPath(filePath: string): string;
5
9
  /** Resolve relative imports to absolute paths for use in virtual components */
6
10
  export declare function resolveImportsToAbsolute(imports: string[], docFilePath: string): string[];
7
11
  /** Generate a virtual Svelte iframe wrapper component for a snippet.
@@ -1,4 +1,4 @@
1
- import { dirname, resolve } from 'node:path';
1
+ import { dirname, relative, resolve, sep } from 'node:path';
2
2
  /** Base64url encode a string (URL-safe, no padding) */
3
3
  export function base64urlEncode(str) {
4
4
  return Buffer.from(str).toString('base64url');
@@ -7,6 +7,22 @@ export function base64urlEncode(str) {
7
7
  export function base64urlDecode(str) {
8
8
  return Buffer.from(str, 'base64url').toString('utf-8');
9
9
  }
10
+ // Doc paths are encoded relative to this root. Encoding absolute paths would
11
+ // leak the machine's filesystem layout into published URLs and produce path
12
+ // segments long enough to break static hosts (GitHub Pages rejects them).
13
+ let docPathRoot = process.cwd();
14
+ /** Set the root that doc paths are encoded against (the Vite root / staging dir) */
15
+ export function setDocPathRoot(root) {
16
+ docPathRoot = root;
17
+ }
18
+ /** Encode a doc file path for use in URLs and emitted file names */
19
+ export function encodeDocPath(filePath) {
20
+ return base64urlEncode(relative(docPathRoot, filePath).split(sep).join('/'));
21
+ }
22
+ /** Decode an encoded doc path back to an absolute path */
23
+ function decodeDocPath(encoded) {
24
+ return resolve(docPathRoot, base64urlDecode(encoded));
25
+ }
10
26
  /** Resolve relative imports to absolute paths for use in virtual components */
11
27
  export function resolveImportsToAbsolute(imports, docFilePath) {
12
28
  const docDir = dirname(docFilePath);
@@ -150,19 +166,19 @@ export function generateStaticPreviewHtml(scriptSrc, cssLinks) {
150
166
  }
151
167
  /** Build the virtual module ID for an iframe wrapper component */
152
168
  export function iframeVirtualId(docFilePath, snippetName) {
153
- return `/@sdocs/iframe/${base64urlEncode(docFilePath)}/${snippetName}.svelte`;
169
+ return `/@sdocs/iframe/${encodeDocPath(docFilePath)}/${snippetName}.svelte`;
154
170
  }
155
171
  /** Build the preview URL for an iframe HTML page (dev mode) */
156
172
  export function previewUrl(docFilePath, snippetName) {
157
- return `/@sdocs/preview/${base64urlEncode(docFilePath)}/${snippetName}`;
173
+ return `/@sdocs/preview/${encodeDocPath(docFilePath)}/${snippetName}`;
158
174
  }
159
175
  /** Build the preview URL for static build output */
160
176
  export function buildPreviewUrl(docFilePath, snippetName) {
161
- return `/previews/${base64urlEncode(docFilePath)}/${snippetName}.html`;
177
+ return `/previews/${encodeDocPath(docFilePath)}/${snippetName}.html`;
162
178
  }
163
179
  /** Virtual module ID for a preview's mount script (embedded production builds) */
164
180
  export function mountVirtualId(docFilePath, snippetName) {
165
- return `/@sdocs/mount/${base64urlEncode(docFilePath)}/${snippetName}.js`;
181
+ return `/@sdocs/mount/${encodeDocPath(docFilePath)}/${snippetName}.js`;
166
182
  }
167
183
  /** Parse a mount virtual ID back into its parts */
168
184
  export function parseMountId(id) {
@@ -170,7 +186,7 @@ export function parseMountId(id) {
170
186
  if (!match)
171
187
  return null;
172
188
  return {
173
- docFilePath: base64urlDecode(match[1]),
189
+ docFilePath: decodeDocPath(match[1]),
174
190
  snippetName: match[2],
175
191
  };
176
192
  }
@@ -180,7 +196,7 @@ export function parseIframeId(id) {
180
196
  if (!match)
181
197
  return null;
182
198
  return {
183
- docFilePath: base64urlDecode(match[1]),
199
+ docFilePath: decodeDocPath(match[1]),
184
200
  snippetName: match[2],
185
201
  };
186
202
  }
@@ -190,7 +206,7 @@ export function parsePreviewUrl(url) {
190
206
  if (!match)
191
207
  return null;
192
208
  return {
193
- docFilePath: base64urlDecode(match[1]),
209
+ docFilePath: decodeDocPath(match[1]),
194
210
  snippetName: match[2],
195
211
  };
196
212
  }
package/dist/vite.js CHANGED
@@ -6,7 +6,7 @@ import { parseComponent } from './server/prop-parser.js';
6
6
  import { extractSnippets, extractMarkupBody, hasDefaultSnippet, generateAutoDefault, } from './server/snippet-extractor.js';
7
7
  import { highlight, disposeHighlighter } from './server/highlighter.js';
8
8
  import { extractTocFromHtml } from './server/toc-extractor.js';
9
- import { parseIframeId, parseMountId, parsePreviewUrl, resolveImportsToAbsolute, generateIframeComponent, generateMountScript, generatePreviewHtml, generateStaticPreviewHtml, iframeVirtualId, mountVirtualId, previewUrl, buildPreviewUrl, base64urlEncode, } from './server/snippet-compiler.js';
9
+ import { parseIframeId, parseMountId, parsePreviewUrl, resolveImportsToAbsolute, generateIframeComponent, generateMountScript, generatePreviewHtml, generateStaticPreviewHtml, iframeVirtualId, mountVirtualId, previewUrl, buildPreviewUrl, encodeDocPath, setDocPathRoot, } from './server/snippet-compiler.js';
10
10
  const VIRTUAL_MODULE_ID = 'virtual:sdocs';
11
11
  const RESOLVED_VIRTUAL_ID = '\0virtual:sdocs';
12
12
  const IFRAME_PREFIX = '/@sdocs/iframe/';
@@ -31,6 +31,7 @@ export function sdocsPlugin(userConfig) {
31
31
  name: 'sdocs',
32
32
  async configResolved(resolvedConfig) {
33
33
  root = resolvedConfig.root;
34
+ setDocPathRoot(root);
34
35
  isBuild = resolvedConfig.command === 'build';
35
36
  isSsrBuild = !!resolvedConfig.build?.ssr;
36
37
  base = resolvedConfig.base || '/';
@@ -158,7 +159,7 @@ export function sdocsPlugin(userConfig) {
158
159
  }
159
160
  }
160
161
  for (const entry of docEntries.values()) {
161
- const encoded = base64urlEncode(entry.filePath);
162
+ const encoded = encodeDocPath(entry.filePath);
162
163
  for (const snippet of entry.snippets) {
163
164
  const jsFileName = `previews/${encoded}/${snippet.name}.js`;
164
165
  this.emitFile({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdocs",
3
- "version": "0.0.33",
3
+ "version": "0.0.34",
4
4
  "description": "A lightweight documentation tool for Svelte 5 components",
5
5
  "type": "module",
6
6
  "license": "MIT",