@vyriy/ssg 0.9.1 → 0.9.3
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/AGENTS.md +4 -0
- package/README.md +43 -93
- package/build-static-site.js +191 -0
- package/components.d.ts +40 -0
- package/components.js +343 -0
- package/content-data.d.ts +69 -0
- package/{content.js → content-data.js} +17 -100
- package/content-section.d.ts +11 -0
- package/content-section.js +104 -0
- package/index.d.ts +12 -8
- package/index.js +8 -7
- package/json-ld.d.ts +11 -0
- package/json-ld.js +36 -0
- package/llm.d.ts +12 -0
- package/llm.js +59 -0
- package/markdown-page.d.ts +5 -0
- package/markdown-page.js +55 -0
- package/minify-html.d.ts +1 -0
- package/minify-html.js +51 -0
- package/package.json +109 -62
- package/parse-page.d.ts +1 -1
- package/parse-page.js +9 -9
- package/paths.d.ts +11 -0
- package/paths.js +16 -0
- package/render-page.d.ts +40 -0
- package/render-page.js +193 -0
- package/robots.js +1 -0
- package/sitemap.js +31 -11
- package/types.d.ts +2 -54
- package/bin/index.js +0 -3
- package/cli.d.ts +0 -7
- package/cli.js +0 -84
- package/content.d.ts +0 -43
- package/html.d.ts +0 -29
- package/html.js +0 -228
- package/markdown.d.ts +0 -3
- package/markdown.js +0 -31
- package/ssg.js +0 -199
- /package/{ssg.d.ts → build-static-site.d.ts} +0 -0
- /package/{plain.d.ts → markdown-plain-text.d.ts} +0 -0
- /package/{plain.js → markdown-plain-text.js} +0 -0
package/paths.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type StaticSitePaths = {
|
|
2
|
+
readonly consultingOutputPath: string;
|
|
3
|
+
readonly consultingSourcePath: string;
|
|
4
|
+
readonly docOutputPath: string;
|
|
5
|
+
readonly docSourcePath: string;
|
|
6
|
+
readonly outputDirectory: string;
|
|
7
|
+
readonly outputPath: string;
|
|
8
|
+
readonly projectRoot: string;
|
|
9
|
+
readonly sourcePath: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const getStaticSitePaths: (cwd: string) => StaticSitePaths;
|
package/paths.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { basename, dirname, join } from 'node:path';
|
|
2
|
+
export const getStaticSitePaths = (cwd) => {
|
|
3
|
+
const isDistRuntime = basename(cwd) === 'dist';
|
|
4
|
+
const projectRoot = isDistRuntime ? dirname(cwd) : cwd;
|
|
5
|
+
const outputDirectory = isDistRuntime ? cwd : join(cwd, 'dist');
|
|
6
|
+
return {
|
|
7
|
+
consultingOutputPath: join(outputDirectory, 'consulting/index.html'),
|
|
8
|
+
consultingSourcePath: join(projectRoot, 'site/consulting/README.md'),
|
|
9
|
+
docOutputPath: join(outputDirectory, 'docs/index.html'),
|
|
10
|
+
docSourcePath: join(projectRoot, 'site/docs/README.md'),
|
|
11
|
+
outputDirectory,
|
|
12
|
+
outputPath: join(outputDirectory, 'index.html'),
|
|
13
|
+
projectRoot,
|
|
14
|
+
sourcePath: join(projectRoot, 'site/home/README.md'),
|
|
15
|
+
};
|
|
16
|
+
};
|
package/render-page.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { HomePageFeaturedContentItem, RelatedDocument } from './content-data.js';
|
|
2
|
+
import type { ContentEntry, ContentSection, PageData } from './types.js';
|
|
3
|
+
type RenderDocumentOptions = {
|
|
4
|
+
readonly discoveryLinks?: readonly DiscoveryLink[];
|
|
5
|
+
readonly googleAnalyticsMeasurementId?: string;
|
|
6
|
+
readonly markdownAlternateHref?: string;
|
|
7
|
+
readonly robotsDirective?: string;
|
|
8
|
+
readonly siteUrl?: string;
|
|
9
|
+
readonly socialMetadata?: SocialMetadata;
|
|
10
|
+
readonly stylesheetContent?: string;
|
|
11
|
+
readonly stylesheetHref?: string;
|
|
12
|
+
};
|
|
13
|
+
type RenderPageOptions = RenderDocumentOptions & {
|
|
14
|
+
readonly canonicalPath?: string;
|
|
15
|
+
readonly featured?: readonly HomePageFeaturedContentItem[];
|
|
16
|
+
readonly related?: readonly RelatedDocument[];
|
|
17
|
+
readonly showTags?: boolean;
|
|
18
|
+
};
|
|
19
|
+
type RenderContentIndexOptions = RenderDocumentOptions & {
|
|
20
|
+
readonly page?: number;
|
|
21
|
+
readonly pages?: number;
|
|
22
|
+
};
|
|
23
|
+
type DiscoveryLink = {
|
|
24
|
+
readonly href: string;
|
|
25
|
+
readonly rel: string;
|
|
26
|
+
readonly type: string;
|
|
27
|
+
};
|
|
28
|
+
type SocialMetadata = {
|
|
29
|
+
readonly description?: string;
|
|
30
|
+
readonly imageAlt: string;
|
|
31
|
+
readonly imagePath: string;
|
|
32
|
+
readonly siteName?: string;
|
|
33
|
+
readonly title?: string;
|
|
34
|
+
};
|
|
35
|
+
export declare const renderPage: (page: PageData, { canonicalPath, discoveryLinks, featured, googleAnalyticsMeasurementId, markdownAlternateHref, related, showTags, siteUrl, socialMetadata, stylesheetContent, stylesheetHref, }?: RenderPageOptions) => string;
|
|
36
|
+
export declare const renderNotFoundPage: ({ googleAnalyticsMeasurementId, siteUrl, stylesheetContent, stylesheetHref, }?: RenderDocumentOptions) => string;
|
|
37
|
+
export declare const getContentIndexHref: (section: ContentSection, page: number) => string;
|
|
38
|
+
export declare const renderContentIndex: (section: ContentSection, entries: readonly ContentEntry[], { googleAnalyticsMeasurementId, page, pages, siteUrl, stylesheetContent, stylesheetHref, }?: RenderContentIndexOptions) => string;
|
|
39
|
+
export declare const renderSearchPage: ({ googleAnalyticsMeasurementId, siteUrl, stylesheetContent, stylesheetHref, }?: RenderDocumentOptions) => string;
|
|
40
|
+
export {};
|
package/render-page.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { html as renderDocumentHtml } from '@vyriy/html';
|
|
3
|
+
import { html as renderReactHtml } from '@vyriy/render/html';
|
|
4
|
+
import ReactMarkdown from 'react-markdown';
|
|
5
|
+
import rehypeHighlight from 'rehype-highlight';
|
|
6
|
+
import remarkGfm from 'remark-gfm';
|
|
7
|
+
import { Card, Catalog, NotFoundPage, Page, SearchPage } from './components.js';
|
|
8
|
+
import { getWebPageJsonLd, renderJsonLdScript } from './json-ld.js';
|
|
9
|
+
import { minifyHtml } from './minify-html.js';
|
|
10
|
+
import { getAbsoluteUrl } from './sitemap.js';
|
|
11
|
+
const escapedStyleCloseTag = ['<', String.fromCodePoint(92), '/style'].join('');
|
|
12
|
+
const googleAnalyticsMeasurementIdPattern = /^G-[A-Z0-9]+$/;
|
|
13
|
+
const internalMarkdownLinkHostnames = new Set(['vyriy.dev', 'vyriy.local']);
|
|
14
|
+
const videoAssetExtensionPattern = /\.(mp4|mov|webm)(?:[?#].*)?$/iu;
|
|
15
|
+
const renderDocument = (title, description, body, { canonicalPath = '/', discoveryLinks = [], googleAnalyticsMeasurementId, markdownAlternateHref, robotsDirective, siteUrl, socialMetadata, stylesheetContent, stylesheetHref = '/styles.css', } = {}) => {
|
|
16
|
+
return minifyHtml(renderDocumentHtml({
|
|
17
|
+
htmlAttributes: 'lang="en"',
|
|
18
|
+
title: renderReactHtml(_jsx("title", { children: title })),
|
|
19
|
+
meta: [
|
|
20
|
+
'<meta charset="utf-8" />',
|
|
21
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1" />',
|
|
22
|
+
renderReactHtml(_jsx("meta", { content: description, name: "description" })),
|
|
23
|
+
robotsDirective ? renderReactHtml(_jsx("meta", { content: robotsDirective, name: "robots" })) : '',
|
|
24
|
+
renderSocialMetadata(title, description, canonicalPath, siteUrl, socialMetadata),
|
|
25
|
+
].join('\n'),
|
|
26
|
+
link: [
|
|
27
|
+
renderStylesheet(stylesheetHref, stylesheetContent),
|
|
28
|
+
canonicalPath ? renderReactHtml(_jsx("link", { href: getAbsoluteUrl(canonicalPath, siteUrl), rel: "canonical" })) : '',
|
|
29
|
+
markdownAlternateHref
|
|
30
|
+
? renderReactHtml(_jsx("link", { href: markdownAlternateHref, rel: "alternate", type: "text/markdown" }))
|
|
31
|
+
: '',
|
|
32
|
+
...discoveryLinks.map((link) => renderReactHtml(_jsx("link", { href: link.href, rel: link.rel, type: link.type }))),
|
|
33
|
+
renderReactHtml(_jsx("link", { href: "/favicon.ico", rel: "icon", sizes: "any" })),
|
|
34
|
+
renderReactHtml(_jsx("link", { href: "/favicon-16x16.png", rel: "icon", sizes: "16x16" })),
|
|
35
|
+
renderReactHtml(_jsx("link", { href: "/favicon-32x32.png", rel: "icon", sizes: "32x32" })),
|
|
36
|
+
renderReactHtml(_jsx("link", { href: "/apple-touch-icon.png", rel: "apple-touch-icon" })),
|
|
37
|
+
renderGoogleAnalyticsTag(googleAnalyticsMeasurementId),
|
|
38
|
+
].join('\n'),
|
|
39
|
+
body,
|
|
40
|
+
script: renderJsonLdScript(getWebPageJsonLd({
|
|
41
|
+
canonicalPath,
|
|
42
|
+
description,
|
|
43
|
+
siteUrl,
|
|
44
|
+
title,
|
|
45
|
+
})),
|
|
46
|
+
}));
|
|
47
|
+
};
|
|
48
|
+
const renderSocialMetadata = (title, description, canonicalPath, siteUrl, metadata) => {
|
|
49
|
+
if (!metadata) {
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
const socialTitle = metadata.title ?? title;
|
|
53
|
+
const socialDescription = metadata.description ?? description;
|
|
54
|
+
const socialImageUrl = getAbsoluteUrl(metadata.imagePath, siteUrl);
|
|
55
|
+
const canonicalUrl = getAbsoluteUrl(canonicalPath, siteUrl);
|
|
56
|
+
return [
|
|
57
|
+
renderReactHtml(_jsx("meta", { content: "website", property: "og:type" })),
|
|
58
|
+
renderReactHtml(_jsx("meta", { content: metadata.siteName ?? 'Vyriy', property: "og:site_name" })),
|
|
59
|
+
renderReactHtml(_jsx("meta", { content: socialTitle, property: "og:title" })),
|
|
60
|
+
renderReactHtml(_jsx("meta", { content: socialDescription, property: "og:description" })),
|
|
61
|
+
renderReactHtml(_jsx("meta", { content: canonicalUrl, property: "og:url" })),
|
|
62
|
+
renderReactHtml(_jsx("meta", { content: socialImageUrl, property: "og:image" })),
|
|
63
|
+
renderReactHtml(_jsx("meta", { content: metadata.imageAlt, property: "og:image:alt" })),
|
|
64
|
+
renderReactHtml(_jsx("meta", { content: "summary_large_image", name: "twitter:card" })),
|
|
65
|
+
renderReactHtml(_jsx("meta", { content: socialTitle, name: "twitter:title" })),
|
|
66
|
+
renderReactHtml(_jsx("meta", { content: socialDescription, name: "twitter:description" })),
|
|
67
|
+
renderReactHtml(_jsx("meta", { content: socialImageUrl, name: "twitter:image" })),
|
|
68
|
+
].join('\n');
|
|
69
|
+
};
|
|
70
|
+
const renderStylesheet = (stylesheetHref, stylesheetContent) => {
|
|
71
|
+
if (stylesheetContent) {
|
|
72
|
+
return `<style>${stylesheetContent.trim().replaceAll('</style', escapedStyleCloseTag)}</style>`;
|
|
73
|
+
}
|
|
74
|
+
return renderReactHtml(_jsx("link", { href: stylesheetHref, rel: "stylesheet" }));
|
|
75
|
+
};
|
|
76
|
+
const isExternalMarkdownHref = (href) => {
|
|
77
|
+
if (!href) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const url = new URL(href);
|
|
82
|
+
return (url.protocol === 'http:' || url.protocol === 'https:') && !internalMarkdownLinkHostnames.has(url.hostname);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const isVideoAsset = (src) => Boolean(src && videoAssetExtensionPattern.test(src));
|
|
89
|
+
const markdownComponents = {
|
|
90
|
+
a: (props) => {
|
|
91
|
+
const anchorProps = { ...props };
|
|
92
|
+
delete anchorProps.node;
|
|
93
|
+
const externalLinkProps = isExternalMarkdownHref(anchorProps.href) ? { rel: 'noreferrer', target: '_blank' } : {};
|
|
94
|
+
return (_jsx("a", { ...anchorProps, ...externalLinkProps, children: anchorProps.children }));
|
|
95
|
+
},
|
|
96
|
+
img: (props) => {
|
|
97
|
+
const imageProps = { ...props };
|
|
98
|
+
delete imageProps.node;
|
|
99
|
+
const { alt = '', src, title } = imageProps;
|
|
100
|
+
if (!isVideoAsset(src)) {
|
|
101
|
+
return _jsx("img", { ...imageProps, alt: alt });
|
|
102
|
+
}
|
|
103
|
+
return (_jsx("video", { "aria-label": alt, controls: true, preload: "metadata", src: src, title: title, children: _jsx("track", { kind: "captions" }) }));
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
const renderGoogleAnalyticsTag = (measurementId) => {
|
|
107
|
+
if (!measurementId) {
|
|
108
|
+
return '';
|
|
109
|
+
}
|
|
110
|
+
if (!googleAnalyticsMeasurementIdPattern.test(measurementId)) {
|
|
111
|
+
throw new Error(`Invalid Google Analytics measurement ID: ${measurementId}`);
|
|
112
|
+
}
|
|
113
|
+
return [
|
|
114
|
+
renderReactHtml(_jsx("script", { async: true, src: `https://www.googletagmanager.com/gtag/js?id=${measurementId}` })),
|
|
115
|
+
[
|
|
116
|
+
'<script>',
|
|
117
|
+
'window.dataLayer = window.dataLayer || [];',
|
|
118
|
+
'function gtag(){dataLayer.push(arguments);}',
|
|
119
|
+
"gtag('js', new Date());",
|
|
120
|
+
`gtag('config', '${measurementId}');`,
|
|
121
|
+
'</script>',
|
|
122
|
+
].join(''),
|
|
123
|
+
].join('');
|
|
124
|
+
};
|
|
125
|
+
export const renderPage = (page, { canonicalPath = '/', discoveryLinks, featured = [], googleAnalyticsMeasurementId, markdownAlternateHref, related = [], showTags = false, siteUrl, socialMetadata, stylesheetContent, stylesheetHref = '/styles.css', } = {}) => {
|
|
126
|
+
const body = renderReactHtml(_jsx(Page, { content: _jsx(ReactMarkdown, { components: markdownComponents, rehypePlugins: [rehypeHighlight], remarkPlugins: [remarkGfm], children: page.content }), featured: featured.map((item) => ({
|
|
127
|
+
description: item.description,
|
|
128
|
+
href: item.url,
|
|
129
|
+
title: item.title,
|
|
130
|
+
})), related: related.map((item) => ({
|
|
131
|
+
description: item.description,
|
|
132
|
+
href: item.url,
|
|
133
|
+
title: item.title,
|
|
134
|
+
})), tags: showTags ? page.tags : [] }));
|
|
135
|
+
return renderDocument(page.title, page.description, body, {
|
|
136
|
+
canonicalPath,
|
|
137
|
+
discoveryLinks,
|
|
138
|
+
googleAnalyticsMeasurementId,
|
|
139
|
+
markdownAlternateHref,
|
|
140
|
+
siteUrl,
|
|
141
|
+
socialMetadata,
|
|
142
|
+
stylesheetContent,
|
|
143
|
+
stylesheetHref,
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
export const renderNotFoundPage = ({ googleAnalyticsMeasurementId, siteUrl, stylesheetContent, stylesheetHref = '/styles.css', } = {}) => {
|
|
147
|
+
const body = renderReactHtml(_jsx(NotFoundPage, {}));
|
|
148
|
+
return renderDocument('Page not found', 'The requested Vyriy page could not be found.', body, {
|
|
149
|
+
canonicalPath: '',
|
|
150
|
+
googleAnalyticsMeasurementId,
|
|
151
|
+
siteUrl,
|
|
152
|
+
stylesheetContent,
|
|
153
|
+
stylesheetHref,
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
const getSectionTitle = (section) => {
|
|
157
|
+
if (section === 'blog') {
|
|
158
|
+
return 'Blog';
|
|
159
|
+
}
|
|
160
|
+
if (section === 'docs') {
|
|
161
|
+
return 'Documentation';
|
|
162
|
+
}
|
|
163
|
+
return 'Examples';
|
|
164
|
+
};
|
|
165
|
+
export const getContentIndexHref = (section, page) => {
|
|
166
|
+
return page <= 1 ? `/${section}/` : `/${section}/${page}/`;
|
|
167
|
+
};
|
|
168
|
+
export const renderContentIndex = (section, entries, { googleAnalyticsMeasurementId, page = 1, pages = 1, siteUrl, stylesheetContent, stylesheetHref = '/styles.css', } = {}) => {
|
|
169
|
+
const title = getSectionTitle(section);
|
|
170
|
+
const body = renderReactHtml(_jsx(Catalog, { content: entries.map((entry) => (_jsx(Card, { date: entry.date || undefined, description: entry.description, href: entry.href, tags: entry.tags, title: entry.title }, entry.href))), paginate: {
|
|
171
|
+
getHref: (targetPage) => getContentIndexHref(section, targetPage),
|
|
172
|
+
page,
|
|
173
|
+
pages,
|
|
174
|
+
} }));
|
|
175
|
+
return renderDocument(`Vyriy ${title}`, `${title} from Vyriy.`, body, {
|
|
176
|
+
canonicalPath: getContentIndexHref(section, page),
|
|
177
|
+
googleAnalyticsMeasurementId,
|
|
178
|
+
siteUrl,
|
|
179
|
+
stylesheetContent,
|
|
180
|
+
stylesheetHref,
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
export const renderSearchPage = ({ googleAnalyticsMeasurementId, siteUrl, stylesheetContent, stylesheetHref = '/styles.css', } = {}) => {
|
|
184
|
+
const body = renderReactHtml(_jsx(SearchPage, {}));
|
|
185
|
+
return renderDocument('Vyriy Search', 'Search articles and documentation from Vyriy.', body, {
|
|
186
|
+
canonicalPath: '',
|
|
187
|
+
googleAnalyticsMeasurementId,
|
|
188
|
+
robotsDirective: 'noindex, follow',
|
|
189
|
+
siteUrl,
|
|
190
|
+
stylesheetContent,
|
|
191
|
+
stylesheetHref,
|
|
192
|
+
});
|
|
193
|
+
};
|
package/robots.js
CHANGED
package/sitemap.js
CHANGED
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
const defaultSiteUrl = 'https://vyriy.dev';
|
|
4
|
-
const xmlCharacterPattern = /[&<>"']/gu;
|
|
5
|
-
const xmlCharacterEntities = {
|
|
6
|
-
'&': '&',
|
|
7
|
-
'"': '"',
|
|
8
|
-
"'": ''',
|
|
9
|
-
'<': '<',
|
|
10
|
-
'>': '>',
|
|
11
|
-
};
|
|
12
4
|
const escapeXml = (value) => {
|
|
13
|
-
|
|
5
|
+
let escaped = '';
|
|
6
|
+
for (const character of value) {
|
|
7
|
+
if (character === '&') {
|
|
8
|
+
escaped += '&';
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (character === '<') {
|
|
12
|
+
escaped += '<';
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (character === '>') {
|
|
16
|
+
escaped += '>';
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (character === '"') {
|
|
20
|
+
escaped += '"';
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (character === "'") {
|
|
24
|
+
escaped += ''';
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
escaped += character;
|
|
28
|
+
}
|
|
29
|
+
return escaped;
|
|
30
|
+
};
|
|
31
|
+
const normalizeSiteUrl = (siteUrl) => {
|
|
32
|
+
return siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl;
|
|
33
|
+
};
|
|
34
|
+
const normalizePath = (path) => {
|
|
35
|
+
return path.startsWith('/') ? path : `/${path}`;
|
|
14
36
|
};
|
|
15
|
-
const normalizeSiteUrl = (siteUrl) => (siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl);
|
|
16
|
-
const normalizePath = (path) => (path.startsWith('/') ? path : `/${path}`);
|
|
17
37
|
export const getAbsoluteUrl = (path, siteUrl = defaultSiteUrl) => {
|
|
18
38
|
return `${normalizeSiteUrl(siteUrl)}${normalizePath(path)}`;
|
|
19
39
|
};
|
package/types.d.ts
CHANGED
|
@@ -10,38 +10,18 @@ export type PageData = {
|
|
|
10
10
|
readonly title: string;
|
|
11
11
|
readonly updatedAt?: string;
|
|
12
12
|
};
|
|
13
|
-
export type StaticSitePage = {
|
|
14
|
-
readonly canonicalPath?: string;
|
|
15
|
-
readonly inputPath: string;
|
|
16
|
-
readonly outputPath: string;
|
|
17
|
-
};
|
|
18
|
-
export type StaticSiteSection = {
|
|
19
|
-
readonly description?: string;
|
|
20
|
-
readonly index?: boolean;
|
|
21
|
-
readonly pageSize?: number;
|
|
22
|
-
readonly path: string;
|
|
23
|
-
readonly title: string;
|
|
24
|
-
};
|
|
25
13
|
export type BuildStaticSiteOptions = {
|
|
26
|
-
readonly contentPath?: string;
|
|
27
|
-
readonly copyPublic?: boolean;
|
|
28
14
|
readonly cwd?: string;
|
|
29
|
-
readonly defaultTitle?: string;
|
|
30
|
-
readonly footerText?: string;
|
|
31
15
|
readonly googleAnalyticsMeasurementId?: string;
|
|
32
|
-
readonly homePage?: StaticSitePage;
|
|
33
16
|
readonly outputPath?: string;
|
|
34
|
-
readonly pages?: readonly StaticSitePage[];
|
|
35
|
-
readonly publicPath?: string;
|
|
36
|
-
readonly sections?: readonly StaticSiteSection[];
|
|
37
|
-
readonly siteName?: string;
|
|
38
17
|
readonly siteUrl?: string;
|
|
39
18
|
readonly stylesheetContent?: string;
|
|
19
|
+
readonly sourcePath?: string;
|
|
40
20
|
readonly stylesheetHref?: string;
|
|
41
21
|
};
|
|
22
|
+
export type ContentSection = 'blog' | 'docs' | 'examples';
|
|
42
23
|
export type ContentEntry = PageData & {
|
|
43
24
|
readonly href: string;
|
|
44
|
-
readonly section: string;
|
|
45
25
|
readonly slug: string;
|
|
46
26
|
};
|
|
47
27
|
export type ContentSectionBuildResult = {
|
|
@@ -51,35 +31,3 @@ export type ContentSectionBuildResult = {
|
|
|
51
31
|
export type SitemapUrl = {
|
|
52
32
|
readonly path: string;
|
|
53
33
|
};
|
|
54
|
-
export type SearchDocument = {
|
|
55
|
-
readonly content: string;
|
|
56
|
-
readonly date?: string;
|
|
57
|
-
readonly description: string;
|
|
58
|
-
readonly id: string;
|
|
59
|
-
readonly section: string;
|
|
60
|
-
readonly slug: string;
|
|
61
|
-
readonly tags: readonly string[];
|
|
62
|
-
readonly title: string;
|
|
63
|
-
readonly updatedAt?: string;
|
|
64
|
-
readonly url: string;
|
|
65
|
-
};
|
|
66
|
-
export type RelatedDocument = {
|
|
67
|
-
readonly description: string;
|
|
68
|
-
readonly score: number;
|
|
69
|
-
readonly section: string;
|
|
70
|
-
readonly slug: string;
|
|
71
|
-
readonly tags: readonly string[];
|
|
72
|
-
readonly title: string;
|
|
73
|
-
readonly url: string;
|
|
74
|
-
};
|
|
75
|
-
export type RelatedDocumentsMap = Record<string, readonly RelatedDocument[]>;
|
|
76
|
-
export type HomePageFeaturedContentItem = {
|
|
77
|
-
readonly date?: string;
|
|
78
|
-
readonly description: string;
|
|
79
|
-
readonly homePageOrder?: number;
|
|
80
|
-
readonly section: string;
|
|
81
|
-
readonly slug: string;
|
|
82
|
-
readonly tags: readonly string[];
|
|
83
|
-
readonly title: string;
|
|
84
|
-
readonly url: string;
|
|
85
|
-
};
|
package/bin/index.js
DELETED
package/cli.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { BuildStaticSiteOptions } from './types.js';
|
|
2
|
-
type CliOptions = BuildStaticSiteOptions & {
|
|
3
|
-
readonly help?: boolean;
|
|
4
|
-
};
|
|
5
|
-
export declare const parseSsgCliArgs: (args: readonly string[]) => Promise<CliOptions>;
|
|
6
|
-
export declare const runSsgCli: (args?: readonly string[]) => Promise<void>;
|
|
7
|
-
export {};
|
package/cli.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { buildStaticSite } from './ssg.js';
|
|
3
|
-
const helpText = `Usage:
|
|
4
|
-
ssg <content> [options]
|
|
5
|
-
vyriy-ssg <content> [options]
|
|
6
|
-
|
|
7
|
-
Options:
|
|
8
|
-
-o, --output <path> Output directory. Defaults to dist.
|
|
9
|
-
--site-url <url> Absolute site URL for canonical links and sitemap.
|
|
10
|
-
--site-name <name> Site name used by the default theme.
|
|
11
|
-
--stylesheet <href> Stylesheet URL. Omit to use the built-in CSS.
|
|
12
|
-
--stylesheet-file <path> Inline CSS from a local file.
|
|
13
|
-
--ga <id> Google Analytics measurement ID.
|
|
14
|
-
-h, --help Show this help.
|
|
15
|
-
`;
|
|
16
|
-
const readValue = (args, index, flag) => {
|
|
17
|
-
const value = args[index + 1];
|
|
18
|
-
if (!value || value.startsWith('-')) {
|
|
19
|
-
throw new Error(`Missing value for ${flag}.`);
|
|
20
|
-
}
|
|
21
|
-
return value;
|
|
22
|
-
};
|
|
23
|
-
const optionHandlers = {
|
|
24
|
-
'--ga': (args, index, options, arg) => {
|
|
25
|
-
options.googleAnalyticsMeasurementId = readValue(args, index, arg);
|
|
26
|
-
},
|
|
27
|
-
'--output': (args, index, options, arg) => {
|
|
28
|
-
options.outputPath = readValue(args, index, arg);
|
|
29
|
-
},
|
|
30
|
-
'--site-name': (args, index, options, arg) => {
|
|
31
|
-
options.siteName = readValue(args, index, arg);
|
|
32
|
-
},
|
|
33
|
-
'--site-url': (args, index, options, arg) => {
|
|
34
|
-
options.siteUrl = readValue(args, index, arg);
|
|
35
|
-
},
|
|
36
|
-
'--stylesheet': (args, index, options, arg) => {
|
|
37
|
-
options.stylesheetHref = readValue(args, index, arg);
|
|
38
|
-
},
|
|
39
|
-
'--stylesheet-file': async (args, index, options, arg) => {
|
|
40
|
-
options.stylesheetContent = await readFile(readValue(args, index, arg), 'utf8');
|
|
41
|
-
},
|
|
42
|
-
'-o': (args, index, options, arg) => {
|
|
43
|
-
options.outputPath = readValue(args, index, arg);
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
export const parseSsgCliArgs = async (args) => {
|
|
47
|
-
const options = {};
|
|
48
|
-
let contentPath;
|
|
49
|
-
let index = 0;
|
|
50
|
-
while (index < args.length) {
|
|
51
|
-
const arg = args[index] ?? '';
|
|
52
|
-
if (arg === '-h' || arg === '--help') {
|
|
53
|
-
return {
|
|
54
|
-
help: true,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
const optionHandler = optionHandlers[arg];
|
|
58
|
-
if (optionHandler) {
|
|
59
|
-
await optionHandler(args, index, options, arg);
|
|
60
|
-
index += 2;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (arg.startsWith('-')) {
|
|
64
|
-
throw new Error(`Unknown option: ${arg}`);
|
|
65
|
-
}
|
|
66
|
-
if (contentPath) {
|
|
67
|
-
throw new Error(`Unexpected argument: ${arg}`);
|
|
68
|
-
}
|
|
69
|
-
contentPath = arg;
|
|
70
|
-
index += 1;
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
...options,
|
|
74
|
-
...(contentPath ? { contentPath } : {}),
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
export const runSsgCli = async (args = process.argv.slice(2)) => {
|
|
78
|
-
const options = await parseSsgCliArgs(args);
|
|
79
|
-
if (options.help) {
|
|
80
|
-
process.stdout.write(helpText);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
await buildStaticSite(options);
|
|
84
|
-
};
|
package/content.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { BuildStaticSiteOptions, ContentEntry, ContentSectionBuildResult, HomePageFeaturedContentItem, RelatedDocumentsMap, SearchDocument, StaticSiteSection } from './types.js';
|
|
2
|
-
type ContentDataSection = {
|
|
3
|
-
readonly entries: readonly ContentEntry[];
|
|
4
|
-
readonly section: string;
|
|
5
|
-
};
|
|
6
|
-
type RenderOptions = Required<Pick<BuildStaticSiteOptions, 'defaultTitle' | 'footerText' | 'siteName'>> & Pick<BuildStaticSiteOptions, 'googleAnalyticsMeasurementId' | 'siteUrl' | 'stylesheetContent' | 'stylesheetHref'>;
|
|
7
|
-
export declare const contentSearchOptions: {
|
|
8
|
-
boost: {
|
|
9
|
-
content: number;
|
|
10
|
-
description: number;
|
|
11
|
-
tags: number;
|
|
12
|
-
title: number;
|
|
13
|
-
};
|
|
14
|
-
fuzzy: number;
|
|
15
|
-
prefix: boolean;
|
|
16
|
-
};
|
|
17
|
-
export declare const contentMiniSearchOptions: {
|
|
18
|
-
fields: string[];
|
|
19
|
-
storeFields: string[];
|
|
20
|
-
searchOptions: {
|
|
21
|
-
boost: {
|
|
22
|
-
content: number;
|
|
23
|
-
description: number;
|
|
24
|
-
tags: number;
|
|
25
|
-
title: number;
|
|
26
|
-
};
|
|
27
|
-
fuzzy: number;
|
|
28
|
-
prefix: boolean;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
export declare const findReadmePaths: (directory: string) => Promise<readonly string[]>;
|
|
32
|
-
export declare const buildContentEntries: (section: StaticSiteSection, contentPath: string, defaultTitle?: string) => Promise<ContentEntry[]>;
|
|
33
|
-
export declare const buildContentSection: (section: StaticSiteSection, contentPath: string, outputDirectory: string, renderOptions: RenderOptions) => Promise<ContentSectionBuildResult>;
|
|
34
|
-
export declare const writeContentEntryDocuments: (section: StaticSiteSection, entries: readonly ContentEntry[], outputDirectory: string, renderOptions: RenderOptions & {
|
|
35
|
-
readonly relatedDocuments?: RelatedDocumentsMap;
|
|
36
|
-
}) => Promise<void>;
|
|
37
|
-
export declare const getSearchDocuments: (section: string, entries: readonly ContentEntry[]) => readonly SearchDocument[];
|
|
38
|
-
export declare const getSiteSearchDocuments: (sections: readonly ContentDataSection[]) => readonly SearchDocument[];
|
|
39
|
-
export declare const getMiniSearchIndexJson: (documents: readonly SearchDocument[]) => unknown;
|
|
40
|
-
export declare const getRelatedDocumentsMap: (documents: readonly SearchDocument[]) => RelatedDocumentsMap;
|
|
41
|
-
export declare const getHomePageFeaturedContent: (sections: readonly ContentDataSection[]) => readonly HomePageFeaturedContentItem[];
|
|
42
|
-
export declare const writeContentData: (sections: readonly ContentDataSection[], outputDirectory: string) => Promise<void>;
|
|
43
|
-
export {};
|
package/html.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ContentEntry, HomePageFeaturedContentItem, PageData, RelatedDocument } from './types.js';
|
|
2
|
-
type RenderDocumentOptions = {
|
|
3
|
-
readonly canonicalPath?: string;
|
|
4
|
-
readonly footerText: string;
|
|
5
|
-
readonly googleAnalyticsMeasurementId?: string;
|
|
6
|
-
readonly robotsDirective?: string;
|
|
7
|
-
readonly siteName: string;
|
|
8
|
-
readonly siteUrl?: string;
|
|
9
|
-
readonly stylesheetContent?: string;
|
|
10
|
-
readonly stylesheetHref?: string;
|
|
11
|
-
};
|
|
12
|
-
type RenderPageOptions = RenderDocumentOptions & {
|
|
13
|
-
readonly featured?: readonly HomePageFeaturedContentItem[];
|
|
14
|
-
readonly related?: readonly RelatedDocument[];
|
|
15
|
-
readonly showTags?: boolean;
|
|
16
|
-
};
|
|
17
|
-
type RenderContentIndexOptions = RenderDocumentOptions & {
|
|
18
|
-
readonly page?: number;
|
|
19
|
-
readonly pages?: number;
|
|
20
|
-
readonly sectionPath: string;
|
|
21
|
-
readonly sectionTitle: string;
|
|
22
|
-
};
|
|
23
|
-
export declare const defaultStylesheet: string;
|
|
24
|
-
export declare const renderPage: (page: PageData, options: RenderPageOptions) => string;
|
|
25
|
-
export declare const renderContentIndex: (entries: readonly ContentEntry[], { page, pages, sectionPath, sectionTitle, ...documentOptions }: RenderContentIndexOptions) => string;
|
|
26
|
-
export declare const renderNotFoundPage: (options: RenderDocumentOptions) => string;
|
|
27
|
-
export declare const searchScript: string;
|
|
28
|
-
export declare const renderSearchPage: (options: RenderDocumentOptions) => string;
|
|
29
|
-
export {};
|