@geekity/cms 0.3.0
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/LICENSE +21 -0
- package/README.md +870 -0
- package/admin/layouts/_flash.njk +5 -0
- package/admin/layouts/base.njk +27 -0
- package/admin/layouts/dashboard.njk +78 -0
- package/admin/layouts/document-conflict.njk +49 -0
- package/admin/layouts/document-editor.njk +167 -0
- package/admin/layouts/document-list.njk +92 -0
- package/admin/layouts/federation.njk +145 -0
- package/admin/layouts/login.njk +23 -0
- package/admin/layouts/placeholder.njk +10 -0
- package/admin/layouts/settings.njk +116 -0
- package/admin/layouts/setup.njk +27 -0
- package/admin/layouts/shell.njk +37 -0
- package/admin/layouts/users.njk +91 -0
- package/admin/static/admin.css +726 -0
- package/admin/static/editor.js +30 -0
- package/dist/admin/assets.d.ts +26 -0
- package/dist/admin/assets.d.ts.map +1 -0
- package/dist/admin/assets.js +38 -0
- package/dist/admin/assets.js.map +1 -0
- package/dist/admin/credentials.d.ts +28 -0
- package/dist/admin/credentials.d.ts.map +1 -0
- package/dist/admin/credentials.js +39 -0
- package/dist/admin/credentials.js.map +1 -0
- package/dist/admin/documents.d.ts +134 -0
- package/dist/admin/documents.d.ts.map +1 -0
- package/dist/admin/documents.js +613 -0
- package/dist/admin/documents.js.map +1 -0
- package/dist/admin/federation.d.ts +194 -0
- package/dist/admin/federation.d.ts.map +1 -0
- package/dist/admin/federation.js +299 -0
- package/dist/admin/federation.js.map +1 -0
- package/dist/admin/flash.d.ts +24 -0
- package/dist/admin/flash.d.ts.map +1 -0
- package/dist/admin/flash.js +31 -0
- package/dist/admin/flash.js.map +1 -0
- package/dist/admin/index.d.ts +23 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +15 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/passwords.d.ts +49 -0
- package/dist/admin/passwords.d.ts.map +1 -0
- package/dist/admin/passwords.js +122 -0
- package/dist/admin/passwords.js.map +1 -0
- package/dist/admin/preview.d.ts +18 -0
- package/dist/admin/preview.d.ts.map +1 -0
- package/dist/admin/preview.js +83 -0
- package/dist/admin/preview.js.map +1 -0
- package/dist/admin/routes.d.ts +54 -0
- package/dist/admin/routes.d.ts.map +1 -0
- package/dist/admin/routes.js +383 -0
- package/dist/admin/routes.js.map +1 -0
- package/dist/admin/session.d.ts +44 -0
- package/dist/admin/session.d.ts.map +1 -0
- package/dist/admin/session.js +69 -0
- package/dist/admin/session.js.map +1 -0
- package/dist/admin/settings.d.ts +194 -0
- package/dist/admin/settings.d.ts.map +1 -0
- package/dist/admin/settings.js +470 -0
- package/dist/admin/settings.js.map +1 -0
- package/dist/admin/store.d.ts +407 -0
- package/dist/admin/store.d.ts.map +1 -0
- package/dist/admin/store.js +704 -0
- package/dist/admin/store.js.map +1 -0
- package/dist/admin/templates.d.ts +36 -0
- package/dist/admin/templates.d.ts.map +1 -0
- package/dist/admin/templates.js +43 -0
- package/dist/admin/templates.js.map +1 -0
- package/dist/admin/uploads.d.ts +96 -0
- package/dist/admin/uploads.d.ts.map +1 -0
- package/dist/admin/uploads.js +179 -0
- package/dist/admin/uploads.js.map +1 -0
- package/dist/admin/users.d.ts +95 -0
- package/dist/admin/users.d.ts.map +1 -0
- package/dist/admin/users.js +233 -0
- package/dist/admin/users.js.map +1 -0
- package/dist/cli.d.ts +85 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +424 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +172 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +184 -0
- package/dist/config.js.map +1 -0
- package/dist/content/document.d.ts +66 -0
- package/dist/content/document.d.ts.map +1 -0
- package/dist/content/document.js +13 -0
- package/dist/content/document.js.map +1 -0
- package/dist/content/index.d.ts +17 -0
- package/dist/content/index.d.ts.map +1 -0
- package/dist/content/index.js +10 -0
- package/dist/content/index.js.map +1 -0
- package/dist/content/markdown.d.ts +3 -0
- package/dist/content/markdown.d.ts.map +1 -0
- package/dist/content/markdown.js +39 -0
- package/dist/content/markdown.js.map +1 -0
- package/dist/content/media.d.ts +65 -0
- package/dist/content/media.d.ts.map +1 -0
- package/dist/content/media.js +143 -0
- package/dist/content/media.js.map +1 -0
- package/dist/content/parser.d.ts +24 -0
- package/dist/content/parser.d.ts.map +1 -0
- package/dist/content/parser.js +165 -0
- package/dist/content/parser.js.map +1 -0
- package/dist/content/save.d.ts +60 -0
- package/dist/content/save.d.ts.map +1 -0
- package/dist/content/save.js +103 -0
- package/dist/content/save.js.map +1 -0
- package/dist/content/slug.d.ts +25 -0
- package/dist/content/slug.d.ts.map +1 -0
- package/dist/content/slug.js +75 -0
- package/dist/content/slug.js.map +1 -0
- package/dist/content/store.d.ts +133 -0
- package/dist/content/store.d.ts.map +1 -0
- package/dist/content/store.js +411 -0
- package/dist/content/store.js.map +1 -0
- package/dist/content/sync.d.ts +146 -0
- package/dist/content/sync.d.ts.map +1 -0
- package/dist/content/sync.js +421 -0
- package/dist/content/sync.js.map +1 -0
- package/dist/content/writer.d.ts +37 -0
- package/dist/content/writer.d.ts.map +1 -0
- package/dist/content/writer.js +97 -0
- package/dist/content/writer.js.map +1 -0
- package/dist/env.d.ts +48 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +2 -0
- package/dist/env.js.map +1 -0
- package/dist/federation/actor.d.ts +55 -0
- package/dist/federation/actor.d.ts.map +1 -0
- package/dist/federation/actor.js +101 -0
- package/dist/federation/actor.js.map +1 -0
- package/dist/federation/article.d.ts +83 -0
- package/dist/federation/article.d.ts.map +1 -0
- package/dist/federation/article.js +169 -0
- package/dist/federation/article.js.map +1 -0
- package/dist/federation/delivery.d.ts +90 -0
- package/dist/federation/delivery.d.ts.map +1 -0
- package/dist/federation/delivery.js +289 -0
- package/dist/federation/delivery.js.map +1 -0
- package/dist/federation/federation.d.ts +75 -0
- package/dist/federation/federation.d.ts.map +1 -0
- package/dist/federation/federation.js +190 -0
- package/dist/federation/federation.js.map +1 -0
- package/dist/federation/followers.d.ts +43 -0
- package/dist/federation/followers.d.ts.map +1 -0
- package/dist/federation/followers.js +69 -0
- package/dist/federation/followers.js.map +1 -0
- package/dist/federation/inbox.d.ts +68 -0
- package/dist/federation/inbox.d.ts.map +1 -0
- package/dist/federation/inbox.js +171 -0
- package/dist/federation/inbox.js.map +1 -0
- package/dist/federation/index.d.ts +14 -0
- package/dist/federation/index.d.ts.map +1 -0
- package/dist/federation/index.js +10 -0
- package/dist/federation/index.js.map +1 -0
- package/dist/federation/keys.d.ts +27 -0
- package/dist/federation/keys.d.ts.map +1 -0
- package/dist/federation/keys.js +60 -0
- package/dist/federation/keys.js.map +1 -0
- package/dist/federation/mount.d.ts +23 -0
- package/dist/federation/mount.d.ts.map +1 -0
- package/dist/federation/mount.js +71 -0
- package/dist/federation/mount.js.map +1 -0
- package/dist/federation/paths.d.ts +85 -0
- package/dist/federation/paths.d.ts.map +1 -0
- package/dist/federation/paths.js +97 -0
- package/dist/federation/paths.js.map +1 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +71 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +125 -0
- package/dist/init.js.map +1 -0
- package/dist/web/assets.d.ts +65 -0
- package/dist/web/assets.d.ts.map +1 -0
- package/dist/web/assets.js +152 -0
- package/dist/web/assets.js.map +1 -0
- package/dist/web/context.d.ts +121 -0
- package/dist/web/context.d.ts.map +1 -0
- package/dist/web/context.js +112 -0
- package/dist/web/context.js.map +1 -0
- package/dist/web/documents.d.ts +28 -0
- package/dist/web/documents.d.ts.map +1 -0
- package/dist/web/documents.js +39 -0
- package/dist/web/documents.js.map +1 -0
- package/dist/web/feeds.d.ts +122 -0
- package/dist/web/feeds.d.ts.map +1 -0
- package/dist/web/feeds.js +229 -0
- package/dist/web/feeds.js.map +1 -0
- package/dist/web/index.d.ts +17 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +10 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/negotiate.d.ts +219 -0
- package/dist/web/negotiate.d.ts.map +1 -0
- package/dist/web/negotiate.js +409 -0
- package/dist/web/negotiate.js.map +1 -0
- package/dist/web/pagination.d.ts +48 -0
- package/dist/web/pagination.d.ts.map +1 -0
- package/dist/web/pagination.js +30 -0
- package/dist/web/pagination.js.map +1 -0
- package/dist/web/render.d.ts +72 -0
- package/dist/web/render.d.ts.map +1 -0
- package/dist/web/render.js +73 -0
- package/dist/web/render.js.map +1 -0
- package/dist/web/routes.d.ts +31 -0
- package/dist/web/routes.d.ts.map +1 -0
- package/dist/web/routes.js +366 -0
- package/dist/web/routes.js.map +1 -0
- package/dist/web/templates.d.ts +34 -0
- package/dist/web/templates.d.ts.map +1 -0
- package/dist/web/templates.js +106 -0
- package/dist/web/templates.js.map +1 -0
- package/package.json +68 -0
- package/templates/site/content/_data/site.json +8 -0
- package/templates/site/content/pages/about.md +11 -0
- package/templates/site/content/pages/pages.json +3 -0
- package/templates/site/content/posts/2026-01-01-hello-world.md +34 -0
- package/templates/site/content/posts/posts.json +4 -0
- package/templates/site/geekity.config.ts +11 -0
- package/templates/site/gitignore +3 -0
- package/templates/site/server.ts +20 -0
- package/templates/site/tsconfig.json +18 -0
- package/themes/default/README.md +148 -0
- package/themes/default/layouts/404.njk +9 -0
- package/themes/default/layouts/base.njk +59 -0
- package/themes/default/layouts/home.njk +9 -0
- package/themes/default/layouts/page.njk +13 -0
- package/themes/default/layouts/post.njk +30 -0
- package/themes/default/layouts/tag.njk +19 -0
- package/themes/default/partials/pagination.njk +14 -0
- package/themes/default/partials/post-list.njk +24 -0
- package/themes/default/partials/tags.njk +12 -0
- package/themes/default/static/style.css +327 -0
package/dist/init.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { cp, mkdir, readdir, rename, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
/**
|
|
6
|
+
* The scaffolding `geekity init` copies, resolved from this module rather than
|
|
7
|
+
* from the working directory so it is found whether the CLI is running from
|
|
8
|
+
* `src/` under tsx or from `dist/` inside `node_modules`.
|
|
9
|
+
*/
|
|
10
|
+
export const SITE_TEMPLATE_DIR = fileURLToPath(new URL('../templates/site/', import.meta.url));
|
|
11
|
+
/**
|
|
12
|
+
* `.gitignore` cannot be shipped under its own name: npm rewrites a
|
|
13
|
+
* `.gitignore` inside a package to `.npmignore` when it packs it, so the
|
|
14
|
+
* template carries it as `gitignore` and init renames it back.
|
|
15
|
+
*/
|
|
16
|
+
const DOTFILES = new Map([['gitignore', '.gitignore']]);
|
|
17
|
+
/** Thrown when the target directory already holds something. */
|
|
18
|
+
export class DirectoryNotEmptyError extends Error {
|
|
19
|
+
directory;
|
|
20
|
+
name = 'DirectoryNotEmptyError';
|
|
21
|
+
constructor(directory) {
|
|
22
|
+
super(`${directory} is not empty. geekity init only writes into a new or empty directory, so it can never overwrite a site you already have.`);
|
|
23
|
+
this.directory = directory;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a new site.
|
|
28
|
+
*
|
|
29
|
+
* The template is copied as it is; only `package.json` and
|
|
30
|
+
* `pnpm-workspace.yaml` are generated, because a nested manifest or workspace
|
|
31
|
+
* file inside `templates/` would be picked up by the workspace and by every
|
|
32
|
+
* tool that walks for one.
|
|
33
|
+
*/
|
|
34
|
+
export async function initSite(options) {
|
|
35
|
+
const cwd = options.cwd ?? process.cwd();
|
|
36
|
+
const directory = path.resolve(cwd, options.directory);
|
|
37
|
+
if (!(await isEmptyOrMissing(directory)))
|
|
38
|
+
throw new DirectoryNotEmptyError(directory);
|
|
39
|
+
await mkdir(directory, { recursive: true });
|
|
40
|
+
await cp(SITE_TEMPLATE_DIR, directory, { recursive: true });
|
|
41
|
+
for (const [packed, real] of DOTFILES) {
|
|
42
|
+
await rename(path.join(directory, packed), path.join(directory, real));
|
|
43
|
+
}
|
|
44
|
+
const manifest = siteManifest({
|
|
45
|
+
name: options.name ?? path.basename(directory),
|
|
46
|
+
version: options.version ?? ownManifest().version,
|
|
47
|
+
});
|
|
48
|
+
await writeFile(path.join(directory, 'package.json'), `${JSON.stringify(manifest, undefined, 2)}\n`, 'utf8');
|
|
49
|
+
await writeFile(path.join(directory, 'pnpm-workspace.yaml'), pnpmSettings(), 'utf8');
|
|
50
|
+
return { directory, files: (await readdir(directory)).sort() };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* pnpm settings a new site starts with, as `pnpm-workspace.yaml`.
|
|
54
|
+
*
|
|
55
|
+
* pnpm reads its settings from this file even for a single package, and from
|
|
56
|
+
* version 11 reads nothing else: the `pnpm` field in `package.json` is
|
|
57
|
+
* ignored. Two things are settled here so a fresh install runs quietly and
|
|
58
|
+
* unattended. Other package managers ignore the file.
|
|
59
|
+
*/
|
|
60
|
+
export function pnpmSettings() {
|
|
61
|
+
return [
|
|
62
|
+
'# pnpm settings for this site. Other package managers ignore this file.',
|
|
63
|
+
'',
|
|
64
|
+
'# tsx pulls in esbuild, whose install script pnpm blocks until it is',
|
|
65
|
+
'# allowed by name.',
|
|
66
|
+
'allowBuilds:',
|
|
67
|
+
' esbuild: true',
|
|
68
|
+
'',
|
|
69
|
+
'# nunjucks names chokidar an optional peer and only reaches for it when a',
|
|
70
|
+
"# FileSystemLoader is built with `watch: true`. The CMS's loader never is.",
|
|
71
|
+
'peerDependencyRules:',
|
|
72
|
+
' allowedVersions:',
|
|
73
|
+
" nunjucks>chokidar: '5'",
|
|
74
|
+
'',
|
|
75
|
+
].join('\n');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The manifest a new site starts life with.
|
|
79
|
+
*
|
|
80
|
+
* The three devDependencies are the TypeScript toolchain the template needs:
|
|
81
|
+
* `tsx` runs `server.ts` and loads a TypeScript config for the bin, and
|
|
82
|
+
* `typescript` plus `@types/node` are what type checking the site takes. Their
|
|
83
|
+
* ranges are this package's own, so a site starts on the versions the CMS was
|
|
84
|
+
* built and tested against rather than on a range frozen in the source.
|
|
85
|
+
*/
|
|
86
|
+
export function siteManifest(options) {
|
|
87
|
+
const toolchain = ownManifest().devDependencies ?? {};
|
|
88
|
+
return {
|
|
89
|
+
name: options.name,
|
|
90
|
+
version: '0.1.0',
|
|
91
|
+
private: true,
|
|
92
|
+
type: 'module',
|
|
93
|
+
scripts: {
|
|
94
|
+
dev: 'tsx watch server.ts',
|
|
95
|
+
start: 'tsx server.ts',
|
|
96
|
+
sync: 'geekity sync',
|
|
97
|
+
},
|
|
98
|
+
dependencies: {
|
|
99
|
+
'@geekity/cms': `^${options.version}`,
|
|
100
|
+
},
|
|
101
|
+
devDependencies: {
|
|
102
|
+
'@types/node': toolchain['@types/node'] ?? '^24.0.0',
|
|
103
|
+
tsx: toolchain['tsx'] ?? '^4.20.0',
|
|
104
|
+
typescript: toolchain['typescript'] ?? '^5.9.0',
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** True when the directory does not exist, or exists with nothing in it. */
|
|
109
|
+
async function isEmptyOrMissing(directory) {
|
|
110
|
+
try {
|
|
111
|
+
return (await readdir(directory)).length === 0;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const code = error?.code;
|
|
115
|
+
if (code === 'ENOENT')
|
|
116
|
+
return true;
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** This package's own manifest, read from beside the module that is running. */
|
|
121
|
+
export function ownManifest() {
|
|
122
|
+
const require = createRequire(import.meta.url);
|
|
123
|
+
return require('../package.json');
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Stats } from 'node:fs';
|
|
2
|
+
/** URL prefix the theme's own files are served under. */
|
|
3
|
+
export declare const THEME_ASSET_PREFIX = "/theme/";
|
|
4
|
+
/**
|
|
5
|
+
* URL prefix uploads are served under, and the content subdirectory they live
|
|
6
|
+
* in. They are the same word on purpose: doc's Eleventy config copies
|
|
7
|
+
* `content/uploads/` through to `/uploads/`, so a Markdown link the editor
|
|
8
|
+
* writes has to resolve to the same URL whether the site is being served by the
|
|
9
|
+
* CMS or built by Eleventy.
|
|
10
|
+
*/
|
|
11
|
+
export declare const UPLOAD_ASSET_PREFIX = "/uploads/";
|
|
12
|
+
/** The content subdirectory {@link UPLOAD_ASSET_PREFIX} is served from. */
|
|
13
|
+
export declare const UPLOAD_DIRECTORY = "uploads";
|
|
14
|
+
/** How long a browser may keep an upload. A day: the bytes at a URL never change. */
|
|
15
|
+
export declare const UPLOAD_ASSET_MAX_AGE = 86400;
|
|
16
|
+
/** One file under `content/uploads/`, or `undefined`. */
|
|
17
|
+
export declare function findUpload(relative: string, contentDir: string): StaticAsset | undefined;
|
|
18
|
+
/** Directory inside a theme that holds the files served over HTTP. */
|
|
19
|
+
export declare const THEME_STATIC_DIR = "static";
|
|
20
|
+
/** How long a browser may keep a theme asset. One hour. */
|
|
21
|
+
export declare const THEME_ASSET_MAX_AGE = 3600;
|
|
22
|
+
/** A file on disk, located and described, ready to be served. */
|
|
23
|
+
export interface StaticAsset {
|
|
24
|
+
/** Absolute path on disk. */
|
|
25
|
+
file: string;
|
|
26
|
+
/** Size and modification time, for the validators. */
|
|
27
|
+
stats: Stats;
|
|
28
|
+
/** Media type, from the extension. */
|
|
29
|
+
contentType: string;
|
|
30
|
+
/** Strong-looking validator over size and mtime. */
|
|
31
|
+
etag: string;
|
|
32
|
+
}
|
|
33
|
+
/** A theme file. The theme's assets are ordinary {@link StaticAsset}s. */
|
|
34
|
+
export type ThemeAsset = StaticAsset;
|
|
35
|
+
/**
|
|
36
|
+
* Find one file under a list of roots, first root that has it winning.
|
|
37
|
+
*
|
|
38
|
+
* Returns `undefined` when the request escapes its root, names a directory, or
|
|
39
|
+
* matches nothing. Traversal is checked after resolution rather than by
|
|
40
|
+
* inspecting the request, so an encoded `..` cannot slip past.
|
|
41
|
+
*/
|
|
42
|
+
export declare function findAsset(relative: string, roots: string[]): StaticAsset | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Find one asset, site theme first and packaged theme second, the same order
|
|
45
|
+
* templates resolve in.
|
|
46
|
+
*/
|
|
47
|
+
export declare function findThemeAsset(relative: string, themeDirs: string[]): ThemeAsset | undefined;
|
|
48
|
+
/** The theme directories to search, in order: the site's, then the package's. */
|
|
49
|
+
export declare function themeSearchPath(siteThemeDir: string): string[];
|
|
50
|
+
/** How long a browser may keep an asset, when the caller does not say. */
|
|
51
|
+
export interface AssetResponseOptions {
|
|
52
|
+
/** Seconds. Defaults to {@link THEME_ASSET_MAX_AGE}. */
|
|
53
|
+
maxAge?: number | undefined;
|
|
54
|
+
}
|
|
55
|
+
/** The response body and headers for an asset. */
|
|
56
|
+
export declare function assetResponse(asset: StaticAsset, options?: AssetResponseOptions): Response;
|
|
57
|
+
/** The 304 an unchanged asset gets, which carries the validators and no body. */
|
|
58
|
+
export declare function assetNotModified(asset: StaticAsset, options?: AssetResponseOptions): Response;
|
|
59
|
+
/** {@link assetResponse} at the theme's cache lifetime. */
|
|
60
|
+
export declare function themeAssetResponse(asset: ThemeAsset): Response;
|
|
61
|
+
/** {@link assetNotModified} at the theme's cache lifetime. */
|
|
62
|
+
export declare function themeAssetNotModified(asset: ThemeAsset): Response;
|
|
63
|
+
/** Whether a request's `If-None-Match` covers this asset. */
|
|
64
|
+
export declare function matchesEtag(ifNoneMatch: string | undefined, etag: string): boolean;
|
|
65
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/web/assets.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAMrC,yDAAyD;AACzD,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAE5C;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAE/C,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C,qFAAqF;AACrF,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAE1C,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAExF;AAED,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,WAAW,CAAC;AAEzC,2DAA2D;AAC3D,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC,iEAAiE;AACjE,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,KAAK,EAAE,KAAK,CAAC;IACb,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,0EAA0E;AAC1E,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC;AAErC;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,GAAG,SAAS,CA6BpF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,SAAS,CAK5F;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9D;AAED,0EAA0E;AAC1E,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,GAAE,oBAAyB,GAAG,QAAQ,CAG9F;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,GAAE,oBAAyB,GAAG,QAAQ,CAKjG;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,CAE9D;AAED,8DAA8D;AAC9D,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,CAEjE;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAOlF"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { createReadStream, statSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
|
+
import { PACKAGED_THEME_DIR } from './templates.js';
|
|
5
|
+
/** URL prefix the theme's own files are served under. */
|
|
6
|
+
export const THEME_ASSET_PREFIX = '/theme/';
|
|
7
|
+
/**
|
|
8
|
+
* URL prefix uploads are served under, and the content subdirectory they live
|
|
9
|
+
* in. They are the same word on purpose: doc's Eleventy config copies
|
|
10
|
+
* `content/uploads/` through to `/uploads/`, so a Markdown link the editor
|
|
11
|
+
* writes has to resolve to the same URL whether the site is being served by the
|
|
12
|
+
* CMS or built by Eleventy.
|
|
13
|
+
*/
|
|
14
|
+
export const UPLOAD_ASSET_PREFIX = '/uploads/';
|
|
15
|
+
/** The content subdirectory {@link UPLOAD_ASSET_PREFIX} is served from. */
|
|
16
|
+
export const UPLOAD_DIRECTORY = 'uploads';
|
|
17
|
+
/** How long a browser may keep an upload. A day: the bytes at a URL never change. */
|
|
18
|
+
export const UPLOAD_ASSET_MAX_AGE = 86400;
|
|
19
|
+
/** One file under `content/uploads/`, or `undefined`. */
|
|
20
|
+
export function findUpload(relative, contentDir) {
|
|
21
|
+
return findAsset(relative, [path.join(contentDir, UPLOAD_DIRECTORY)]);
|
|
22
|
+
}
|
|
23
|
+
/** Directory inside a theme that holds the files served over HTTP. */
|
|
24
|
+
export const THEME_STATIC_DIR = 'static';
|
|
25
|
+
/** How long a browser may keep a theme asset. One hour. */
|
|
26
|
+
export const THEME_ASSET_MAX_AGE = 3600;
|
|
27
|
+
/**
|
|
28
|
+
* Find one file under a list of roots, first root that has it winning.
|
|
29
|
+
*
|
|
30
|
+
* Returns `undefined` when the request escapes its root, names a directory, or
|
|
31
|
+
* matches nothing. Traversal is checked after resolution rather than by
|
|
32
|
+
* inspecting the request, so an encoded `..` cannot slip past.
|
|
33
|
+
*/
|
|
34
|
+
export function findAsset(relative, roots) {
|
|
35
|
+
const normalized = normalizeAssetPath(relative);
|
|
36
|
+
if (normalized === undefined)
|
|
37
|
+
return undefined;
|
|
38
|
+
for (const directory of roots) {
|
|
39
|
+
const root = path.resolve(directory);
|
|
40
|
+
const file = path.resolve(root, normalized);
|
|
41
|
+
// `path.resolve` has already collapsed every `..`; anything that landed
|
|
42
|
+
// outside the root was trying to get out.
|
|
43
|
+
if (file !== root && !file.startsWith(root + path.sep))
|
|
44
|
+
continue;
|
|
45
|
+
let stats;
|
|
46
|
+
try {
|
|
47
|
+
stats = statSync(file);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!stats.isFile())
|
|
53
|
+
continue;
|
|
54
|
+
return {
|
|
55
|
+
file,
|
|
56
|
+
stats,
|
|
57
|
+
contentType: contentTypeFor(file),
|
|
58
|
+
etag: `"${stats.size.toString(16)}-${Math.trunc(stats.mtimeMs).toString(16)}"`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Find one asset, site theme first and packaged theme second, the same order
|
|
65
|
+
* templates resolve in.
|
|
66
|
+
*/
|
|
67
|
+
export function findThemeAsset(relative, themeDirs) {
|
|
68
|
+
return findAsset(relative, themeDirs.map((themeDir) => path.join(themeDir, THEME_STATIC_DIR)));
|
|
69
|
+
}
|
|
70
|
+
/** The theme directories to search, in order: the site's, then the package's. */
|
|
71
|
+
export function themeSearchPath(siteThemeDir) {
|
|
72
|
+
return [siteThemeDir, PACKAGED_THEME_DIR];
|
|
73
|
+
}
|
|
74
|
+
/** The response body and headers for an asset. */
|
|
75
|
+
export function assetResponse(asset, options = {}) {
|
|
76
|
+
const body = Readable.toWeb(createReadStream(asset.file));
|
|
77
|
+
return new Response(body, { headers: assetHeaders(asset, options) });
|
|
78
|
+
}
|
|
79
|
+
/** The 304 an unchanged asset gets, which carries the validators and no body. */
|
|
80
|
+
export function assetNotModified(asset, options = {}) {
|
|
81
|
+
const headers = assetHeaders(asset, options);
|
|
82
|
+
headers.delete('content-length');
|
|
83
|
+
headers.delete('content-type');
|
|
84
|
+
return new Response(null, { status: 304, headers });
|
|
85
|
+
}
|
|
86
|
+
/** {@link assetResponse} at the theme's cache lifetime. */
|
|
87
|
+
export function themeAssetResponse(asset) {
|
|
88
|
+
return assetResponse(asset);
|
|
89
|
+
}
|
|
90
|
+
/** {@link assetNotModified} at the theme's cache lifetime. */
|
|
91
|
+
export function themeAssetNotModified(asset) {
|
|
92
|
+
return assetNotModified(asset);
|
|
93
|
+
}
|
|
94
|
+
/** Whether a request's `If-None-Match` covers this asset. */
|
|
95
|
+
export function matchesEtag(ifNoneMatch, etag) {
|
|
96
|
+
if (ifNoneMatch === undefined)
|
|
97
|
+
return false;
|
|
98
|
+
if (ifNoneMatch.trim() === '*')
|
|
99
|
+
return true;
|
|
100
|
+
return ifNoneMatch
|
|
101
|
+
.split(',')
|
|
102
|
+
.map((candidate) => candidate.trim().replace(/^W\//, ''))
|
|
103
|
+
.includes(etag);
|
|
104
|
+
}
|
|
105
|
+
function assetHeaders(asset, options) {
|
|
106
|
+
const maxAge = options.maxAge ?? THEME_ASSET_MAX_AGE;
|
|
107
|
+
return new Headers({
|
|
108
|
+
'content-type': asset.contentType,
|
|
109
|
+
'content-length': String(asset.stats.size),
|
|
110
|
+
'cache-control': `public, max-age=${String(maxAge)}`,
|
|
111
|
+
'last-modified': new Date(asset.stats.mtimeMs).toUTCString(),
|
|
112
|
+
etag: asset.etag,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A request path under an asset prefix as a path relative to the directory it
|
|
117
|
+
* is served from, or `undefined` when it is not one worth looking up.
|
|
118
|
+
*/
|
|
119
|
+
function normalizeAssetPath(relative) {
|
|
120
|
+
if (relative === '' || relative.endsWith('/'))
|
|
121
|
+
return undefined;
|
|
122
|
+
if (relative.includes('\0'))
|
|
123
|
+
return undefined;
|
|
124
|
+
// A leading slash would make `path.resolve` ignore the theme directory.
|
|
125
|
+
return relative.replace(/^\/+/, '');
|
|
126
|
+
}
|
|
127
|
+
/** Media types for what a theme actually ships. */
|
|
128
|
+
const CONTENT_TYPES = new Map([
|
|
129
|
+
['.css', 'text/css; charset=utf-8'],
|
|
130
|
+
['.js', 'text/javascript; charset=utf-8'],
|
|
131
|
+
['.mjs', 'text/javascript; charset=utf-8'],
|
|
132
|
+
['.json', 'application/json; charset=utf-8'],
|
|
133
|
+
['.map', 'application/json; charset=utf-8'],
|
|
134
|
+
['.svg', 'image/svg+xml'],
|
|
135
|
+
['.png', 'image/png'],
|
|
136
|
+
['.jpg', 'image/jpeg'],
|
|
137
|
+
['.jpeg', 'image/jpeg'],
|
|
138
|
+
['.gif', 'image/gif'],
|
|
139
|
+
['.webp', 'image/webp'],
|
|
140
|
+
['.avif', 'image/avif'],
|
|
141
|
+
['.ico', 'image/x-icon'],
|
|
142
|
+
['.woff', 'font/woff'],
|
|
143
|
+
['.woff2', 'font/woff2'],
|
|
144
|
+
['.ttf', 'font/ttf'],
|
|
145
|
+
['.otf', 'font/otf'],
|
|
146
|
+
['.txt', 'text/plain; charset=utf-8'],
|
|
147
|
+
['.webmanifest', 'application/manifest+json'],
|
|
148
|
+
]);
|
|
149
|
+
function contentTypeFor(file) {
|
|
150
|
+
return CONTENT_TYPES.get(path.extname(file).toLowerCase()) ?? 'application/octet-stream';
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/web/assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ResolvedConfig } from '../config.ts';
|
|
2
|
+
import type { Document } from '../content/document.ts';
|
|
3
|
+
/** Where the site-wide data file lives, relative to the content directory. */
|
|
4
|
+
export declare const SITE_DATA_FILE = "_data/site.json";
|
|
5
|
+
/** How many posts a listing page holds when the site does not say. */
|
|
6
|
+
export declare const DEFAULT_POSTS_PER_PAGE = 10;
|
|
7
|
+
/**
|
|
8
|
+
* Site-wide data, from `content/_data/site.json` when the site has one.
|
|
9
|
+
*
|
|
10
|
+
* Eleventy exposes the same file as the `site` global, so a layout ported from
|
|
11
|
+
* an Eleventy build reads `{{ site.title }}` unchanged. Unknown keys are kept,
|
|
12
|
+
* so a site can put anything in the file and reach it from its templates.
|
|
13
|
+
*/
|
|
14
|
+
export interface SiteData {
|
|
15
|
+
/** Site title. Defaults to `Geekity`. */
|
|
16
|
+
title: string;
|
|
17
|
+
/** One-line description, shown under the title. */
|
|
18
|
+
tagline?: string | undefined;
|
|
19
|
+
/** Public origin. Defaults to the configured `baseUrl`. */
|
|
20
|
+
url: string;
|
|
21
|
+
/** Site author. */
|
|
22
|
+
author?: string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The site's avatar, as the public path it is served at. Empty until one has
|
|
25
|
+
* been uploaded on the settings screen; the ActivityPub actor's `icon`.
|
|
26
|
+
*/
|
|
27
|
+
avatar?: string | undefined;
|
|
28
|
+
/** How many posts a listing page holds. */
|
|
29
|
+
postsPerPage?: number | undefined;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* What Eleventy calls `page`: the data about the document being rendered, as
|
|
34
|
+
* opposed to the document's own front matter.
|
|
35
|
+
*/
|
|
36
|
+
export interface PageContext {
|
|
37
|
+
/** The document's URL path. Always ends in `/`. */
|
|
38
|
+
url: string;
|
|
39
|
+
/** Publish date, absent for a document that has none. */
|
|
40
|
+
date?: Date | undefined;
|
|
41
|
+
/** The permalink's last segment, Eleventy's `page.fileSlug`. */
|
|
42
|
+
fileSlug: string;
|
|
43
|
+
/** The source file, relative to the content directory, Eleventy-style. */
|
|
44
|
+
inputPath: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One document as a template sees it.
|
|
48
|
+
*
|
|
49
|
+
* The shape mirrors what an Eleventy layout receives: the front matter at the
|
|
50
|
+
* top level, the rendered body as `content`, and `page` alongside. Two keys
|
|
51
|
+
* Eleventy puts on a collection item rather than on the page — `url` and
|
|
52
|
+
* `tags` — are here too, so one shape serves both a rendered page and an entry
|
|
53
|
+
* in a listing.
|
|
54
|
+
*/
|
|
55
|
+
export interface DocumentContext {
|
|
56
|
+
/** Display title. */
|
|
57
|
+
title: string;
|
|
58
|
+
/** Publish date, absent for a document that has none. */
|
|
59
|
+
date?: Date | undefined;
|
|
60
|
+
/** Taxonomy, in the order the file lists it. */
|
|
61
|
+
tags: string[];
|
|
62
|
+
/** The Markdown body rendered to HTML. Templates print it with `| safe`. */
|
|
63
|
+
content: string;
|
|
64
|
+
/** The document's URL path, the same value as `page.url`. */
|
|
65
|
+
url: string;
|
|
66
|
+
/** Eleventy's `page`. */
|
|
67
|
+
page: PageContext;
|
|
68
|
+
/** `post` or `page`. */
|
|
69
|
+
type: string;
|
|
70
|
+
/** Everything else from the front matter, including unmodelled keys. */
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A document as a template sees it.
|
|
75
|
+
*
|
|
76
|
+
* Front matter goes on first so the keys the CMS models always win: a file
|
|
77
|
+
* with a stray `content` or `page` key cannot displace the rendered body or
|
|
78
|
+
* the page data.
|
|
79
|
+
*/
|
|
80
|
+
export declare function documentContext(document: Document): DocumentContext;
|
|
81
|
+
/** Reads `content/_data/site.json`, and re-reads it when the file changes. */
|
|
82
|
+
export interface SiteDataSource {
|
|
83
|
+
/** The current site data, defaults filled in. */
|
|
84
|
+
read(): SiteData;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The settings the admin stores, as site data.
|
|
88
|
+
*
|
|
89
|
+
* The admin's settings screen is the source of truth for the values it
|
|
90
|
+
* manages, and `content/_data/site.json` is the mirror it writes for an
|
|
91
|
+
* Eleventy build. Handing the source a reader for the settings is what makes a
|
|
92
|
+
* saved title show on the very next request rather than on the next time the
|
|
93
|
+
* file's modification time is noticed.
|
|
94
|
+
*/
|
|
95
|
+
export interface SiteSettingsSource {
|
|
96
|
+
/** The stored settings as site data. Empty when nothing is stored. */
|
|
97
|
+
read(): Partial<SiteData>;
|
|
98
|
+
}
|
|
99
|
+
/** What {@link createSiteDataSource} reads from besides the config. */
|
|
100
|
+
export interface CreateSiteDataSourceOptions {
|
|
101
|
+
/** The admin's settings, when the CMS has an admin store to read them from. */
|
|
102
|
+
settings?: SiteSettingsSource | undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A source over one site's data.
|
|
106
|
+
*
|
|
107
|
+
* The file is read once and then only again when its modification time moves,
|
|
108
|
+
* so a render costs one `stat` rather than one parse. A file that is missing
|
|
109
|
+
* or will not parse falls back to the defaults instead of failing the request:
|
|
110
|
+
* a typo in `site.json` should not take the site down.
|
|
111
|
+
*
|
|
112
|
+
* The stored settings, when there are any, go on top of the file. They are the
|
|
113
|
+
* same values the file was last written from, so the two normally agree; the
|
|
114
|
+
* overlay is what keeps them agreeing in the moment between a save and the
|
|
115
|
+
* file's `mtime` being noticed, and the file underneath is what carries the
|
|
116
|
+
* keys the settings form does not manage.
|
|
117
|
+
*/
|
|
118
|
+
export declare function createSiteDataSource(config: ResolvedConfig, options?: CreateSiteDataSourceOptions): SiteDataSource;
|
|
119
|
+
/** How many posts a listing page holds, from the site data or the default. */
|
|
120
|
+
export declare function postsPerPage(site: SiteData): number;
|
|
121
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/web/context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,8EAA8E;AAC9E,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACxB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACxB,gDAAgD;IAChD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,eAAe,CA0BnE;AAED,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,IAAI,IAAI,QAAQ,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC3B;AAED,uEAAuE;AACvE,MAAM,WAAW,2BAA2B;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CAC3C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,GAAE,2BAAgC,GACxC,cAAc,CAiChB;AAaD,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAMnD"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/** Where the site-wide data file lives, relative to the content directory. */
|
|
4
|
+
export const SITE_DATA_FILE = '_data/site.json';
|
|
5
|
+
/** How many posts a listing page holds when the site does not say. */
|
|
6
|
+
export const DEFAULT_POSTS_PER_PAGE = 10;
|
|
7
|
+
/**
|
|
8
|
+
* A document as a template sees it.
|
|
9
|
+
*
|
|
10
|
+
* Front matter goes on first so the keys the CMS models always win: a file
|
|
11
|
+
* with a stray `content` or `page` key cannot displace the rendered body or
|
|
12
|
+
* the page data.
|
|
13
|
+
*/
|
|
14
|
+
export function documentContext(document) {
|
|
15
|
+
const date = toDate(document.date);
|
|
16
|
+
return {
|
|
17
|
+
...document.extra,
|
|
18
|
+
permalink: document.permalink,
|
|
19
|
+
slug: document.slug,
|
|
20
|
+
draft: document.draft,
|
|
21
|
+
...optional('description', document.description),
|
|
22
|
+
...optional('author', document.author),
|
|
23
|
+
...optional('updated', toDate(document.updated)),
|
|
24
|
+
...optional('activitypub', document.activitypub),
|
|
25
|
+
title: document.title,
|
|
26
|
+
...optional('date', date),
|
|
27
|
+
tags: document.tags,
|
|
28
|
+
content: document.html,
|
|
29
|
+
url: document.permalink,
|
|
30
|
+
type: document.type,
|
|
31
|
+
page: {
|
|
32
|
+
url: document.permalink,
|
|
33
|
+
...optional('date', date),
|
|
34
|
+
fileSlug: document.slug,
|
|
35
|
+
inputPath: `./${document.path}`,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A source over one site's data.
|
|
41
|
+
*
|
|
42
|
+
* The file is read once and then only again when its modification time moves,
|
|
43
|
+
* so a render costs one `stat` rather than one parse. A file that is missing
|
|
44
|
+
* or will not parse falls back to the defaults instead of failing the request:
|
|
45
|
+
* a typo in `site.json` should not take the site down.
|
|
46
|
+
*
|
|
47
|
+
* The stored settings, when there are any, go on top of the file. They are the
|
|
48
|
+
* same values the file was last written from, so the two normally agree; the
|
|
49
|
+
* overlay is what keeps them agreeing in the moment between a save and the
|
|
50
|
+
* file's `mtime` being noticed, and the file underneath is what carries the
|
|
51
|
+
* keys the settings form does not manage.
|
|
52
|
+
*/
|
|
53
|
+
export function createSiteDataSource(config, options = {}) {
|
|
54
|
+
const file = path.join(config.contentDir, ...SITE_DATA_FILE.split('/'));
|
|
55
|
+
const settings = options.settings;
|
|
56
|
+
let cached = {};
|
|
57
|
+
let cachedAt;
|
|
58
|
+
function fromFile() {
|
|
59
|
+
let modifiedAt;
|
|
60
|
+
try {
|
|
61
|
+
modifiedAt = statSync(file).mtimeMs;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
cachedAt = undefined;
|
|
65
|
+
cached = {};
|
|
66
|
+
return cached;
|
|
67
|
+
}
|
|
68
|
+
if (modifiedAt === cachedAt)
|
|
69
|
+
return cached;
|
|
70
|
+
cachedAt = modifiedAt;
|
|
71
|
+
cached = readSiteFile(file);
|
|
72
|
+
return cached;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
read() {
|
|
76
|
+
// The defaults are rebuilt per read rather than captured, because
|
|
77
|
+
// `config.baseUrl` may have been settled from the settings after this
|
|
78
|
+
// source was made.
|
|
79
|
+
const defaults = { title: 'Geekity', url: config.baseUrl };
|
|
80
|
+
return { ...defaults, ...fromFile(), ...settings?.read() };
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function readSiteFile(file) {
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
87
|
+
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
|
|
88
|
+
? parsed
|
|
89
|
+
: {};
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** How many posts a listing page holds, from the site data or the default. */
|
|
96
|
+
export function postsPerPage(site) {
|
|
97
|
+
const configured = site['postsPerPage'];
|
|
98
|
+
if (typeof configured === 'number' && Number.isInteger(configured) && configured > 0) {
|
|
99
|
+
return configured;
|
|
100
|
+
}
|
|
101
|
+
return DEFAULT_POSTS_PER_PAGE;
|
|
102
|
+
}
|
|
103
|
+
function toDate(value) {
|
|
104
|
+
if (value === undefined)
|
|
105
|
+
return undefined;
|
|
106
|
+
const parsed = new Date(value);
|
|
107
|
+
return Number.isNaN(parsed.getTime()) ? undefined : parsed;
|
|
108
|
+
}
|
|
109
|
+
function optional(key, value) {
|
|
110
|
+
return value === undefined ? {} : { [key]: value };
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/web/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Document } from '../content/document.ts';
|
|
2
|
+
import type { ContentStore } from '../content/store.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Whether the public site may show a document.
|
|
5
|
+
*
|
|
6
|
+
* Two things hide one: `draft: true` in the front matter, and living under
|
|
7
|
+
* `_trash/`. Both stay indexed so the admin can find them; neither is ever
|
|
8
|
+
* served, listed, or fed.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isPublicDocument(document: Document): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The document a public URL resolves to, or `undefined`.
|
|
13
|
+
*
|
|
14
|
+
* This is the single lookup the public site does: every representation of a
|
|
15
|
+
* document — the theme's HTML, the Markdown file, the JSON object — is the
|
|
16
|
+
* same document found the same way, so they cannot disagree about what exists.
|
|
17
|
+
*/
|
|
18
|
+
export declare function publicDocumentAt(store: ContentStore, permalink: string): Document | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* The ActivityStreams id of a document, or `undefined` when it has none.
|
|
21
|
+
*
|
|
22
|
+
* Only a published post federates (doc-4), so only a published post has an id
|
|
23
|
+
* to advertise. This is what the theme's `<link rel="alternate">` points at,
|
|
24
|
+
* and it is deliberately the same string the object dispatcher answers under:
|
|
25
|
+
* the page and the object agree about the post's name in the fediverse.
|
|
26
|
+
*/
|
|
27
|
+
export declare function activityStreamsId(document: Document, baseUrl: string): string | undefined;
|
|
28
|
+
//# sourceMappingURL=documents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/web/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAE5D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAI7F;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGzF"}
|