sdocs 0.0.33 → 0.0.35
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 +21 -0
- package/dist/server/app-gen.js +5 -2
- package/dist/server/snippet-compiler.d.ts +4 -0
- package/dist/server/snippet-compiler.js +24 -8
- package/dist/ui/styles/sdocs.css +15 -0
- package/dist/vite.js +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.0.35] - 2026-07-03
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **The embedded Explorer guards its typography.** Host apps commonly style
|
|
15
|
+
bare `code`, `pre`, and `a` elements; those rules reached into the
|
|
16
|
+
Explorer's UI (shrinking code blocks, recoloring links). The Explorer now
|
|
17
|
+
pins its own font family, sizing, and link color at the `.sdocs-app`
|
|
18
|
+
boundary. Hosts with higher-specificity global rules should still scope
|
|
19
|
+
them away from `.sdocs-app`.
|
|
20
|
+
|
|
21
|
+
## [0.0.34] - 2026-07-03
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **Preview paths are short and project-relative.** Preview URLs and emitted
|
|
26
|
+
file names used to encode the doc's absolute filesystem path, which leaked
|
|
27
|
+
the machine's directory layout into published sites and produced path
|
|
28
|
+
segments long enough for GitHub Pages to reject the deployment. Paths are
|
|
29
|
+
now encoded relative to the project root.
|
|
30
|
+
|
|
10
31
|
## [0.0.33] - 2026-07-03
|
|
11
32
|
|
|
12
33
|
### Added
|
package/dist/server/app-gen.js
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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/${
|
|
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/${
|
|
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/${
|
|
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/${
|
|
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:
|
|
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:
|
|
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:
|
|
209
|
+
docFilePath: decodeDocPath(match[1]),
|
|
194
210
|
snippetName: match[2],
|
|
195
211
|
};
|
|
196
212
|
}
|
package/dist/ui/styles/sdocs.css
CHANGED
|
@@ -13,6 +13,21 @@
|
|
|
13
13
|
color: var(--color-base-900);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/* Guard the Explorer's typography against host element rules when embedded:
|
|
17
|
+
* hosts commonly style bare `code`/`pre`/`a` (e.g. font-size: 0.875em), which
|
|
18
|
+
* would otherwise reach into the Explorer. Sizing stays wrapper-driven. */
|
|
19
|
+
.sdocs-app code,
|
|
20
|
+
.sdocs-app pre {
|
|
21
|
+
font-family: var(--mono);
|
|
22
|
+
font-size: inherit;
|
|
23
|
+
line-height: inherit;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.sdocs-app a {
|
|
27
|
+
color: inherit;
|
|
28
|
+
text-decoration: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
.sdocs-app[data-sdocs-theme='dark'] {
|
|
17
32
|
/* base — layouts, backgrounds, surfaces, borders, text (inverted) */
|
|
18
33
|
--color-base-0: var(--color-zinc-1000);
|
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,
|
|
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 =
|
|
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({
|