@gjsify/site 0.64.14 → 0.64.15
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/README.md +24 -0
- package/lib/render/blog.d.ts +7 -2
- package/lib/render/blog.js +7 -3
- package/lib/render/markdown.d.ts +11 -0
- package/lib/render/markdown.js +4 -0
- package/lib/site.d.ts +7 -0
- package/lib/site.js +3 -3
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -285,6 +285,30 @@ a typo) fails the build, since no Markdown is ever published. Posts are `BlogPos
|
|
|
285
285
|
`article:*` meta, and the landing page shows the newest three of its language. On a site with
|
|
286
286
|
`support` entries, every post ends with a note linking to the support page.
|
|
287
287
|
|
|
288
|
+
### Code blocks
|
|
289
|
+
|
|
290
|
+
A fenced block is rendered plain — `<pre><code class="language-…">` — because the generator
|
|
291
|
+
carries no highlighter. Colours come from a hook the caller supplies, which is what keeps them in
|
|
292
|
+
the HTML instead of in a script on the page:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { buildSite } from '@gjsify/site';
|
|
296
|
+
import { createHighlighter } from '@gjsify/site-highlight';
|
|
297
|
+
|
|
298
|
+
const highlighter = await createHighlighter();
|
|
299
|
+
await buildSite({
|
|
300
|
+
root: process.cwd(),
|
|
301
|
+
out: 'dist',
|
|
302
|
+
markdown: { highlight: (code, lang) => highlighter.highlight(code, lang) },
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
`highlight` receives the block's text and its info string verbatim, and answers either the HTML to
|
|
307
|
+
insert or `undefined` to keep the plain block. The returned HTML is inserted as markup, so the hook
|
|
308
|
+
owns escaping. [`@gjsify/site-highlight`](../site-highlight/README.md) is the ready-made one:
|
|
309
|
+
Shiki on its JavaScript regex engine, the same HTML for light and dark, a Blueprint grammar Shiki
|
|
310
|
+
does not bundle — and a stylesheet that has to be appended after the site's own.
|
|
311
|
+
|
|
288
312
|
## Theme
|
|
289
313
|
|
|
290
314
|
| Key | Meaning |
|
package/lib/render/blog.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type BlogPost, type SiteCommonConfig } from '../site-data/index.js';
|
|
2
2
|
import { type Shell } from './layout.js';
|
|
3
|
+
import { type MarkdownOptions } from './markdown.js';
|
|
3
4
|
type Config = Pick<SiteCommonConfig, 'base'>;
|
|
4
5
|
/** How many posts an app's landing page shows. */
|
|
5
6
|
export declare const LATEST_POSTS = 3;
|
|
@@ -59,6 +60,10 @@ export declare function postKey(post: Pick<BlogPost, 'lang' | 'slug'>): string;
|
|
|
59
60
|
export declare function renderBlogFeed(shell: Shell, options: BlogFeedOptions): string;
|
|
60
61
|
/** Every tag in one language, in first-use order. */
|
|
61
62
|
export declare function tagsOf(posts: readonly BlogPost[], lang: string): string[];
|
|
62
|
-
/**
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Renders a post's Markdown with its URLs resolved. `markdown` reaches the renderer untouched,
|
|
65
|
+
* except for `resolveUrl`, which the post's own URL resolver supplies — that is what makes a
|
|
66
|
+
* relative image or a link to another post's `.md` file work on the published page.
|
|
67
|
+
*/
|
|
68
|
+
export declare function renderPostBody(config: Config, post: BlogPost, postsDir: string, posts: readonly BlogPost[], markdown?: MarkdownOptions): string;
|
|
64
69
|
export {};
|
package/lib/render/blog.js
CHANGED
|
@@ -241,7 +241,11 @@ export function renderBlogFeed(shell, options) {
|
|
|
241
241
|
export function tagsOf(posts, lang) {
|
|
242
242
|
return [...new Set(posts.filter((post) => post.lang === lang).flatMap((post) => post.tags))];
|
|
243
243
|
}
|
|
244
|
-
/**
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Renders a post's Markdown with its URLs resolved. `markdown` reaches the renderer untouched,
|
|
246
|
+
* except for `resolveUrl`, which the post's own URL resolver supplies — that is what makes a
|
|
247
|
+
* relative image or a link to another post's `.md` file work on the published page.
|
|
248
|
+
*/
|
|
249
|
+
export function renderPostBody(config, post, postsDir, posts, markdown = {}) {
|
|
250
|
+
return renderMarkdown(post.body, { ...markdown, resolveUrl: postUrlResolver(config, post, postsDir, posts) });
|
|
247
251
|
}
|
package/lib/render/markdown.d.ts
CHANGED
|
@@ -11,5 +11,16 @@
|
|
|
11
11
|
export interface MarkdownOptions {
|
|
12
12
|
/** Rewrites a link href or image src; absolute and `#` URLs are passed in too. */
|
|
13
13
|
resolveUrl?: (url: string) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Renders one fenced code block instead of the plain `<pre><code>` element — a syntax
|
|
16
|
+
* highlighter, for instance. `lang` is the fence's info string verbatim (empty when the fence
|
|
17
|
+
* names none), so a hook that only knows a few languages can answer `undefined` and leave the
|
|
18
|
+
* block as it is.
|
|
19
|
+
*
|
|
20
|
+
* Returning `undefined` falls back to the plain block. Returned HTML is inserted AS-IS and is
|
|
21
|
+
* therefore never escaped again: the hook owns escaping, because only it knows which parts of
|
|
22
|
+
* its output are markup.
|
|
23
|
+
*/
|
|
24
|
+
highlight?: (code: string, lang: string) => string | undefined;
|
|
14
25
|
}
|
|
15
26
|
export declare function renderMarkdown(source: string, options?: MarkdownOptions): string;
|
package/lib/render/markdown.js
CHANGED
|
@@ -308,6 +308,10 @@ function renderBlock(block, options = {}) {
|
|
|
308
308
|
case 'hr':
|
|
309
309
|
return '<hr>';
|
|
310
310
|
case 'code': {
|
|
311
|
+
const highlighted = options.highlight?.(block.content, block.lang ?? '');
|
|
312
|
+
if (highlighted !== undefined) {
|
|
313
|
+
return highlighted;
|
|
314
|
+
}
|
|
311
315
|
if (block.lang) {
|
|
312
316
|
return `<pre><code class="language-${esc(block.lang)}">${esc(block.content)}</code></pre>`;
|
|
313
317
|
}
|
package/lib/site.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type I18nResult } from './render/i18n.js';
|
|
2
|
+
import type { MarkdownOptions } from './render/markdown.js';
|
|
2
3
|
/** Where a build reads from and writes to. */
|
|
3
4
|
export interface SiteTarget {
|
|
4
5
|
/** Repository root: the root package.json, the metainfo, the screenshots and the icon are read from here. */
|
|
@@ -21,6 +22,12 @@ export interface SiteBuildOptions extends SiteTarget {
|
|
|
21
22
|
githubToken?: string;
|
|
22
23
|
log?: (message: string) => void;
|
|
23
24
|
warn?: (message: string) => void;
|
|
25
|
+
/**
|
|
26
|
+
* How a blog post's Markdown body is rendered. `resolveUrl` is supplied by the build; the
|
|
27
|
+
* useful field here is `highlight`, which lets a consumer colour code blocks without adding
|
|
28
|
+
* client JavaScript to the page. `undefined` keeps the plain `<pre><code>` output.
|
|
29
|
+
*/
|
|
30
|
+
markdown?: MarkdownOptions;
|
|
24
31
|
/** This package's install, for a caller whose module URL does not reveal it — a bundle. */
|
|
25
32
|
packageDir?: string;
|
|
26
33
|
}
|
package/lib/site.js
CHANGED
|
@@ -127,7 +127,7 @@ async function loadPageCatalogs(config, root, self, warn) {
|
|
|
127
127
|
return catalogs;
|
|
128
128
|
}
|
|
129
129
|
/** Reads the posts and copies the files next to them to `<out>/blog/`, where the post URLs point. */
|
|
130
|
-
async function loadBlog(config, target, drafts) {
|
|
130
|
+
async function loadBlog(config, target, drafts, markdown) {
|
|
131
131
|
if (!config.blog)
|
|
132
132
|
return undefined;
|
|
133
133
|
const dir = resolve(target.root, config.blog.posts);
|
|
@@ -136,7 +136,7 @@ async function loadBlog(config, target, drafts) {
|
|
|
136
136
|
await mkdir(dirname(join(target.out, 'blog', file)), { recursive: true });
|
|
137
137
|
await copyFile(join(dir, file), join(target.out, 'blog', file));
|
|
138
138
|
}
|
|
139
|
-
const html = new Map(posts.map((post) => [postKey(post), renderPostBody(config, post, dir, posts)]));
|
|
139
|
+
const html = new Map(posts.map((post) => [postKey(post), renderPostBody(config, post, dir, posts, markdown)]));
|
|
140
140
|
return { posts, html };
|
|
141
141
|
}
|
|
142
142
|
/** Writes the blog of every language and answers its sitemap entries. */
|
|
@@ -200,7 +200,7 @@ export async function buildSite(options) {
|
|
|
200
200
|
const assets = await stageClientAssets(config.base, target.out, self);
|
|
201
201
|
const catalogs = await loadPageCatalogs(config, target.root, self, options.warn);
|
|
202
202
|
const displayFontUrl = await publishDisplayFont(config, target);
|
|
203
|
-
const blog = await loadBlog(config, target, options.drafts ?? false);
|
|
203
|
+
const blog = await loadBlog(config, target, options.drafts ?? false, options.markdown);
|
|
204
204
|
const build = { options, target, cacheDir, assets, catalogs, displayFontUrl, blog };
|
|
205
205
|
return config.kind === 'app' ? buildAppSite(build) : buildDeveloperSite(config, build);
|
|
206
206
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/site",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.15",
|
|
4
4
|
"description": "Static landing page generator for a GNOME-style app, rendered from its AppStream metainfo — downloads, screenshots, release notes, support links, no JavaScript required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -54,21 +54,21 @@
|
|
|
54
54
|
"license": "MIT",
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@girs/gdkpixbuf-2.0": "5.4.0",
|
|
57
|
-
"@gjsify/console": "^0.64.
|
|
58
|
-
"@gjsify/crypto": "^0.64.
|
|
59
|
-
"@gjsify/fs": "^0.64.
|
|
60
|
-
"@gjsify/module": "^0.64.
|
|
61
|
-
"@gjsify/os": "^0.64.
|
|
62
|
-
"@gjsify/path": "^0.64.
|
|
63
|
-
"@gjsify/process": "^0.64.
|
|
64
|
-
"@gjsify/url": "^0.64.
|
|
57
|
+
"@gjsify/console": "^0.64.15",
|
|
58
|
+
"@gjsify/crypto": "^0.64.15",
|
|
59
|
+
"@gjsify/fs": "^0.64.15",
|
|
60
|
+
"@gjsify/module": "^0.64.15",
|
|
61
|
+
"@gjsify/os": "^0.64.15",
|
|
62
|
+
"@gjsify/path": "^0.64.15",
|
|
63
|
+
"@gjsify/process": "^0.64.15",
|
|
64
|
+
"@gjsify/url": "^0.64.15",
|
|
65
65
|
"fast-xml-parser": "^5.11.2",
|
|
66
66
|
"qrcode-generator": "^2.0.4"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@gjsify/cli": "^0.64.
|
|
69
|
+
"@gjsify/cli": "^0.64.15",
|
|
70
70
|
"@gjsify/node-gi": "file:../../node-gi/node-gi",
|
|
71
|
-
"@gjsify/unit": "^0.64.
|
|
71
|
+
"@gjsify/unit": "^0.64.15",
|
|
72
72
|
"@types/node": "^25.9.2",
|
|
73
73
|
"typescript": "^6.0.3"
|
|
74
74
|
},
|