react-cheminfo 0.4.1 → 0.7.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/README.md +198 -34
- package/lib/citation/core/index.d.ts +1 -0
- package/lib/citation/core/index.d.ts.map +1 -1
- package/lib/citation/core/index.js +1 -0
- package/lib/citation/core/index.js.map +1 -1
- package/lib/citation/core/platformPaper.d.ts +14 -0
- package/lib/citation/core/platformPaper.d.ts.map +1 -0
- package/lib/citation/core/platformPaper.js +28 -0
- package/lib/citation/core/platformPaper.js.map +1 -0
- package/lib/color/core/index.d.ts +1 -1
- package/lib/color/core/index.d.ts.map +1 -1
- package/lib/color/core/index.js +1 -1
- package/lib/color/core/index.js.map +1 -1
- package/lib/core.d.ts +1 -0
- package/lib/core.d.ts.map +1 -1
- package/lib/core.js +1 -0
- package/lib/core.js.map +1 -1
- package/lib/ecosystem/core/sites.d.ts +1 -1
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +15 -5
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.js +3 -0
- package/lib/ecosystem/ui/glyphs.js.map +1 -1
- package/lib/orbital/core/index.d.ts +1 -1
- package/lib/orbital/core/index.d.ts.map +1 -1
- package/lib/orbital/core/index.js +1 -1
- package/lib/orbital/core/index.js.map +1 -1
- package/lib/orbital/ui/AtomicOrbitalCanvas.d.ts +5 -0
- package/lib/orbital/ui/AtomicOrbitalCanvas.d.ts.map +1 -1
- package/lib/orbital/ui/AtomicOrbitalCanvas.js +4 -3
- package/lib/orbital/ui/AtomicOrbitalCanvas.js.map +1 -1
- package/lib/orbital/ui/AtomicOrbitalViewer.d.ts +5 -0
- package/lib/orbital/ui/AtomicOrbitalViewer.d.ts.map +1 -1
- package/lib/orbital/ui/AtomicOrbitalViewer.js.map +1 -1
- package/lib/orbital/ui/axesGeometry.d.ts +27 -0
- package/lib/orbital/ui/axesGeometry.d.ts.map +1 -0
- package/lib/orbital/ui/axesGeometry.js +74 -0
- package/lib/orbital/ui/axesGeometry.js.map +1 -0
- package/lib/orbital/ui/camera.d.ts +7 -0
- package/lib/orbital/ui/camera.d.ts.map +1 -1
- package/lib/orbital/ui/camera.js +8 -1
- package/lib/orbital/ui/camera.js.map +1 -1
- package/lib/orbital/ui/renderAxes.d.ts +53 -0
- package/lib/orbital/ui/renderAxes.d.ts.map +1 -0
- package/lib/orbital/ui/renderAxes.js +110 -0
- package/lib/orbital/ui/renderAxes.js.map +1 -0
- package/lib/orbital/ui/viewer.d.ts +16 -0
- package/lib/orbital/ui/viewer.d.ts.map +1 -1
- package/lib/orbital/ui/viewer.js +24 -2
- package/lib/orbital/ui/viewer.js.map +1 -1
- package/lib/periodic/core/categories.d.ts +25 -0
- package/lib/periodic/core/categories.d.ts.map +1 -0
- package/lib/periodic/core/categories.js +70 -0
- package/lib/periodic/core/categories.js.map +1 -0
- package/lib/periodic/core/elements.d.ts +49 -0
- package/lib/periodic/core/elements.d.ts.map +1 -0
- package/lib/periodic/core/elements.js +175 -0
- package/lib/periodic/core/elements.js.map +1 -0
- package/lib/periodic/core/index.d.ts +6 -0
- package/lib/periodic/core/index.d.ts.map +1 -0
- package/lib/periodic/core/index.js +4 -0
- package/lib/periodic/core/index.js.map +1 -0
- package/lib/periodic/core/layout.d.ts +73 -0
- package/lib/periodic/core/layout.d.ts.map +1 -0
- package/lib/periodic/core/layout.js +132 -0
- package/lib/periodic/core/layout.js.map +1 -0
- package/lib/periodic/ui/CategoryLegend.d.ts +26 -0
- package/lib/periodic/ui/CategoryLegend.d.ts.map +1 -0
- package/lib/periodic/ui/CategoryLegend.js +53 -0
- package/lib/periodic/ui/CategoryLegend.js.map +1 -0
- package/lib/periodic/ui/ElementCell.d.ts +53 -0
- package/lib/periodic/ui/ElementCell.d.ts.map +1 -0
- package/lib/periodic/ui/ElementCell.js +56 -0
- package/lib/periodic/ui/ElementCell.js.map +1 -0
- package/lib/periodic/ui/PeriodicTable.d.ts +90 -0
- package/lib/periodic/ui/PeriodicTable.d.ts.map +1 -0
- package/lib/periodic/ui/PeriodicTable.js +77 -0
- package/lib/periodic/ui/PeriodicTable.js.map +1 -0
- package/lib/periodic/ui/PeriodicTableChrome.d.ts +36 -0
- package/lib/periodic/ui/PeriodicTableChrome.d.ts.map +1 -0
- package/lib/periodic/ui/PeriodicTableChrome.js +72 -0
- package/lib/periodic/ui/PeriodicTableChrome.js.map +1 -0
- package/lib/periodic/ui/index.d.ts +7 -0
- package/lib/periodic/ui/index.d.ts.map +1 -0
- package/lib/periodic/ui/index.js +4 -0
- package/lib/periodic/ui/index.js.map +1 -0
- package/lib/seo/core/documentMeta.d.ts +2 -2
- package/lib/seo/core/documentMeta.js +4 -3
- package/lib/seo/core/documentMeta.js.map +1 -1
- package/lib/seo/core/index.d.ts +13 -4
- package/lib/seo/core/index.d.ts.map +1 -1
- package/lib/seo/core/index.js +8 -3
- package/lib/seo/core/index.js.map +1 -1
- package/lib/seo/core/noscript.d.ts +97 -0
- package/lib/seo/core/noscript.d.ts.map +1 -0
- package/lib/seo/core/noscript.js +93 -0
- package/lib/seo/core/noscript.js.map +1 -0
- package/lib/seo/core/pageMeta.d.ts +30 -14
- package/lib/seo/core/pageMeta.d.ts.map +1 -1
- package/lib/seo/core/pageMeta.js +40 -43
- package/lib/seo/core/pageMeta.js.map +1 -1
- package/lib/seo/core/robots.d.ts +55 -0
- package/lib/seo/core/robots.d.ts.map +1 -0
- package/lib/seo/core/robots.js +70 -0
- package/lib/seo/core/robots.js.map +1 -0
- package/lib/seo/core/routes.d.ts +73 -5
- package/lib/seo/core/routes.d.ts.map +1 -1
- package/lib/seo/core/routes.js +142 -16
- package/lib/seo/core/routes.js.map +1 -1
- package/lib/seo/core/siteFiles.d.ts +39 -43
- package/lib/seo/core/siteFiles.d.ts.map +1 -1
- package/lib/seo/core/siteFiles.js +53 -69
- package/lib/seo/core/siteFiles.js.map +1 -1
- package/lib/seo/core/startDocumentMeta.d.ts +44 -0
- package/lib/seo/core/startDocumentMeta.d.ts.map +1 -0
- package/lib/seo/core/startDocumentMeta.js +47 -0
- package/lib/seo/core/startDocumentMeta.js.map +1 -0
- package/lib/seo/core/structuredData.d.ts +48 -0
- package/lib/seo/core/structuredData.d.ts.map +1 -0
- package/lib/seo/core/structuredData.js +41 -0
- package/lib/seo/core/structuredData.js.map +1 -0
- package/lib/seo/core/template.d.ts +48 -0
- package/lib/seo/core/template.d.ts.map +1 -0
- package/lib/seo/core/template.js +53 -0
- package/lib/seo/core/template.js.map +1 -0
- package/lib/seo/vite/ogCard.d.ts +9 -1
- package/lib/seo/vite/ogCard.d.ts.map +1 -1
- package/lib/seo/vite/ogCard.js +14 -4
- package/lib/seo/vite/ogCard.js.map +1 -1
- package/lib/seo/vite/prerender.d.ts +38 -7
- package/lib/seo/vite/prerender.d.ts.map +1 -1
- package/lib/seo/vite/prerender.js +68 -30
- package/lib/seo/vite/prerender.js.map +1 -1
- package/lib/ui.d.ts +1 -0
- package/lib/ui.d.ts.map +1 -1
- package/lib/ui.js +1 -0
- package/lib/ui.js.map +1 -1
- package/package.json +2 -1
- package/src/citation/core/index.ts +1 -0
- package/src/citation/core/platformPaper.ts +32 -0
- package/src/color/core/index.ts +6 -1
- package/src/core.ts +1 -0
- package/src/ecosystem/core/sites.ts +16 -5
- package/src/ecosystem/ui/glyphs.tsx +19 -0
- package/src/orbital/core/index.ts +1 -1
- package/src/orbital/ui/AtomicOrbitalCanvas.tsx +9 -2
- package/src/orbital/ui/AtomicOrbitalViewer.tsx +5 -0
- package/src/orbital/ui/axesGeometry.ts +91 -0
- package/src/orbital/ui/camera.ts +9 -1
- package/src/orbital/ui/renderAxes.ts +190 -0
- package/src/orbital/ui/viewer.ts +32 -2
- package/src/periodic/core/categories.ts +83 -0
- package/src/periodic/core/elements.ts +217 -0
- package/src/periodic/core/index.ts +27 -0
- package/src/periodic/core/layout.ts +183 -0
- package/src/periodic/ui/CategoryLegend.tsx +105 -0
- package/src/periodic/ui/ElementCell.tsx +137 -0
- package/src/periodic/ui/PeriodicTable.tsx +226 -0
- package/src/periodic/ui/PeriodicTableChrome.tsx +170 -0
- package/src/periodic/ui/index.ts +6 -0
- package/src/seo/core/documentMeta.ts +5 -5
- package/src/seo/core/index.ts +19 -12
- package/src/seo/core/noscript.ts +195 -0
- package/src/seo/core/pageMeta.ts +54 -53
- package/src/seo/core/robots.ts +114 -0
- package/src/seo/core/routes.ts +181 -14
- package/src/seo/core/siteFiles.ts +58 -96
- package/src/seo/core/startDocumentMeta.ts +77 -0
- package/src/seo/core/structuredData.ts +80 -0
- package/src/seo/core/template.ts +54 -0
- package/src/seo/vite/ogCard.ts +15 -5
- package/src/seo/vite/prerender.ts +105 -58
- package/src/ui.ts +1 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A readable page for a visitor, or a crawler, with no JavaScript.
|
|
3
|
+
*
|
|
4
|
+
* The body of our sites is an empty root element, so this is the only crawl
|
|
5
|
+
* path through them that costs nothing to render — and it is honest: it says
|
|
6
|
+
* the tool needs JavaScript, and links the addresses it answers.
|
|
7
|
+
*/
|
|
8
|
+
import type { SiteId } from '../../ecosystem/core/sites.ts';
|
|
9
|
+
import type { RouteMeta } from './routes.ts';
|
|
10
|
+
import type { SiteFilesOptions } from './siteFiles.ts';
|
|
11
|
+
/** How the addresses of the site's own pages are written. */
|
|
12
|
+
export type NoscriptHrefs = 'absolute' | 'relative';
|
|
13
|
+
/** A page the block links, and the pages listed under it. */
|
|
14
|
+
export interface NoscriptRoute extends RouteMeta {
|
|
15
|
+
/**
|
|
16
|
+
* Pages listed under this one, as a list nested in its item — the sections of
|
|
17
|
+
* an exercise set under the set itself.
|
|
18
|
+
* @default undefined — the item carries no list
|
|
19
|
+
*/
|
|
20
|
+
children?: readonly NoscriptRoute[];
|
|
21
|
+
}
|
|
22
|
+
/** Which of the family's other sites are listed, and how. */
|
|
23
|
+
export interface NoscriptEcosystem {
|
|
24
|
+
/**
|
|
25
|
+
* The sites listed, in the order they are named. The site writing the block
|
|
26
|
+
* is never one of them, whether or not it is named: the list is headed *Our
|
|
27
|
+
* other tools*.
|
|
28
|
+
* @default every other site in the family, in the family's own order
|
|
29
|
+
*/
|
|
30
|
+
sites?: readonly SiteId[];
|
|
31
|
+
/**
|
|
32
|
+
* Whether each host is followed by ` — ` and the site's one-line tagline.
|
|
33
|
+
* @default true
|
|
34
|
+
*/
|
|
35
|
+
taglines?: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** The prose of the block, where the site says more than its record does. */
|
|
38
|
+
export interface NoscriptText {
|
|
39
|
+
/**
|
|
40
|
+
* The heading the block opens with.
|
|
41
|
+
* @default the site's display name
|
|
42
|
+
*/
|
|
43
|
+
heading?: string;
|
|
44
|
+
/**
|
|
45
|
+
* The paragraph under it, taken as written. It is the one place a reader with
|
|
46
|
+
* no JavaScript is told what the tool is, so it may say more than the tagline
|
|
47
|
+
* — but it still has to say that the tool needs JavaScript.
|
|
48
|
+
* @default the tagline, followed by the sentence naming the requirement
|
|
49
|
+
*/
|
|
50
|
+
intro?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the family's other sites are listed under the site's own pages, and
|
|
53
|
+
* which of them. A crawler that runs no script has no other path from one of
|
|
54
|
+
* our tools to the next, so a site that lists none leaves it with none.
|
|
55
|
+
* `true` lists every other site with its tagline; an object names the sites,
|
|
56
|
+
* or drops the taglines, or both.
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
ecosystem?: boolean | NoscriptEcosystem;
|
|
60
|
+
/**
|
|
61
|
+
* How the site's own addresses are written. `'absolute'` writes them from the
|
|
62
|
+
* root of the host, under the mount the origin names. `'relative'` writes
|
|
63
|
+
* `./exercises` and `./`, which the page resolves against its own `<base>` —
|
|
64
|
+
* the only shape that works for an image whose mount is chosen at container
|
|
65
|
+
* startup, because it bakes no mount into the build at all. The `<base>` such
|
|
66
|
+
* a deployment stamps in ends with a slash, or a relative address resolves
|
|
67
|
+
* one directory too high.
|
|
68
|
+
* @default 'absolute'
|
|
69
|
+
*/
|
|
70
|
+
hrefs?: NoscriptHrefs;
|
|
71
|
+
/**
|
|
72
|
+
* The pages the block links, when they are not the site's whole route table.
|
|
73
|
+
* A crawl path is a menu: a site whose table carries an entry per tutorial
|
|
74
|
+
* step lists the tutorial, not its hundred and thirty-seven steps.
|
|
75
|
+
* @default every route the site answers
|
|
76
|
+
*/
|
|
77
|
+
routes?: readonly NoscriptRoute[];
|
|
78
|
+
}
|
|
79
|
+
/** What the block says, and which addresses it links. */
|
|
80
|
+
export interface NoscriptOptions extends Omit<SiteFilesOptions, 'routes'>, Omit<NoscriptText, 'routes'> {
|
|
81
|
+
/** The addresses it links, each with the label it is linked under. */
|
|
82
|
+
routes: readonly NoscriptRoute[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The `noscript` block, ready to put in the body.
|
|
86
|
+
*
|
|
87
|
+
* An absolute address is written under the mount the deployment named, so a
|
|
88
|
+
* build published as one tool among several on a shared host links its own
|
|
89
|
+
* pages rather than the root of the host it shares.
|
|
90
|
+
* @param options - The site, the pages it links, and the prose that opens the
|
|
91
|
+
* block.
|
|
92
|
+
* @returns The block.
|
|
93
|
+
* @throws {Error} When the deployment named an origin that is not an absolute
|
|
94
|
+
* address.
|
|
95
|
+
*/
|
|
96
|
+
export declare function noscriptIndex(options: NoscriptOptions): string;
|
|
97
|
+
//# sourceMappingURL=noscript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noscript.d.ts","sourceRoot":"","sources":["../../../src/seo/core/noscript.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAiB,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAK3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,UAAU,CAAC;AAEpD,6DAA6D;AAC7D,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACrC;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC;IACxC;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACnC;AAED,yDAAyD;AACzD,MAAM,WAAW,eACf,SAAQ,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC;IACtE,sEAAsE;IACtE,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;CAClC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAc9D"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A readable page for a visitor, or a crawler, with no JavaScript.
|
|
3
|
+
*
|
|
4
|
+
* The body of our sites is an empty root element, so this is the only crawl
|
|
5
|
+
* path through them that costs nothing to render — and it is honest: it says
|
|
6
|
+
* the tool needs JavaScript, and links the addresses it answers.
|
|
7
|
+
*/
|
|
8
|
+
import { siteById, siteDisplayName } from "../../ecosystem/core/lookup.js";
|
|
9
|
+
import { ECOSYSTEM_SITES, siteUrl } from "../../ecosystem/core/sites.js";
|
|
10
|
+
import { joinBasePath } from "../../router/core/basePath.js";
|
|
11
|
+
import { escapeAttribute, escapeText } from "../../share/core/escape.js";
|
|
12
|
+
import { mountPathOf, resolveSite } from "./siteFiles.js";
|
|
13
|
+
/**
|
|
14
|
+
* The `noscript` block, ready to put in the body.
|
|
15
|
+
*
|
|
16
|
+
* An absolute address is written under the mount the deployment named, so a
|
|
17
|
+
* build published as one tool among several on a shared host links its own
|
|
18
|
+
* pages rather than the root of the host it shares.
|
|
19
|
+
* @param options - The site, the pages it links, and the prose that opens the
|
|
20
|
+
* block.
|
|
21
|
+
* @returns The block.
|
|
22
|
+
* @throws {Error} When the deployment named an origin that is not an absolute
|
|
23
|
+
* address.
|
|
24
|
+
*/
|
|
25
|
+
export function noscriptIndex(options) {
|
|
26
|
+
const site = resolveSite(options.site);
|
|
27
|
+
const hrefs = options.hrefs ?? 'absolute';
|
|
28
|
+
const mount = hrefs === 'absolute' ? mountPathOf(options) : '';
|
|
29
|
+
const heading = escapeText(options.heading ?? siteDisplayName(site));
|
|
30
|
+
const intro = escapeText(options.intro ??
|
|
31
|
+
`${site.tagline} This tool needs JavaScript; these are the pages it offers:`);
|
|
32
|
+
return `<noscript>
|
|
33
|
+
<h1>${heading}</h1>
|
|
34
|
+
<p>${intro}</p>${pageList(options.routes, mount, hrefs, ' ')}${familyList(site.id, options.ecosystem)}
|
|
35
|
+
</noscript>`;
|
|
36
|
+
}
|
|
37
|
+
function pageList(routes, mount, hrefs, indent) {
|
|
38
|
+
// A list with no item is not a list: `<ul>` holds at least one `<li>`.
|
|
39
|
+
if (routes.length === 0)
|
|
40
|
+
return '';
|
|
41
|
+
const items = routes
|
|
42
|
+
.map((route) => pageItem(route, mount, hrefs, `${indent} `))
|
|
43
|
+
.join('\n');
|
|
44
|
+
return `\n${indent}<ul>\n${items}\n${indent}</ul>`;
|
|
45
|
+
}
|
|
46
|
+
function pageItem(route, mount, hrefs, indent) {
|
|
47
|
+
const href = escapeAttribute(pageHref(route.path, mount, hrefs));
|
|
48
|
+
const label = escapeText(labelOf(route));
|
|
49
|
+
const note = route.note === undefined || route.note.trim() === ''
|
|
50
|
+
? ''
|
|
51
|
+
: ` — ${escapeText(route.note)}`;
|
|
52
|
+
const children = route.children ?? [];
|
|
53
|
+
const link = `<a href="${href}">${label}</a>${note}`;
|
|
54
|
+
if (children.length === 0)
|
|
55
|
+
return `${indent}<li>${link}</li>`;
|
|
56
|
+
return `${indent}<li>${link}${pageList(children, mount, hrefs, `${indent} `)}
|
|
57
|
+
${indent}</li>`;
|
|
58
|
+
}
|
|
59
|
+
function pageHref(path, mount, hrefs) {
|
|
60
|
+
if (hrefs === 'absolute')
|
|
61
|
+
return joinBasePath(mount, path);
|
|
62
|
+
return `./${path.startsWith('/') ? path.slice(1) : path}`;
|
|
63
|
+
}
|
|
64
|
+
function labelOf(route) {
|
|
65
|
+
const short = route.short;
|
|
66
|
+
return short !== undefined && short.trim() !== '' ? short : route.title;
|
|
67
|
+
}
|
|
68
|
+
function familyList(current, ecosystem) {
|
|
69
|
+
if (ecosystem === undefined || ecosystem === false)
|
|
70
|
+
return '';
|
|
71
|
+
const listed = ecosystem === true ? {} : ecosystem;
|
|
72
|
+
const taglines = listed.taglines ?? true;
|
|
73
|
+
const items = [];
|
|
74
|
+
for (const site of familySites(listed.sites)) {
|
|
75
|
+
if (site.id === current)
|
|
76
|
+
continue;
|
|
77
|
+
const tagline = taglines ? ` — ${escapeText(site.tagline)}` : '';
|
|
78
|
+
items.push(` <li><a href="${escapeAttribute(siteUrl(site))}">${escapeText(site.host)}</a>${tagline}</li>`);
|
|
79
|
+
}
|
|
80
|
+
if (items.length === 0)
|
|
81
|
+
return '';
|
|
82
|
+
return `
|
|
83
|
+
<h2>Our other tools</h2>
|
|
84
|
+
<ul>
|
|
85
|
+
${items.join('\n')}
|
|
86
|
+
</ul>`;
|
|
87
|
+
}
|
|
88
|
+
function familySites(sites) {
|
|
89
|
+
if (sites === undefined)
|
|
90
|
+
return [...ECOSYSTEM_SITES];
|
|
91
|
+
return sites.map((id) => siteById(id));
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=noscript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noscript.js","sourceRoot":"","sources":["../../../src/seo/core/noscript.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAE3E,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAIzE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAiF1D;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,OAAwB;IACpD,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC;IAC1C,MAAM,KAAK,GAAG,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/D,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,UAAU,CACtB,OAAO,CAAC,KAAK;QACX,GAAG,IAAI,CAAC,OAAO,6DAA6D,CAC/E,CAAC;IAEF,OAAO;QACD,OAAO;OACR,KAAK,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,SAAS,CAAC;YAC5F,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CACf,MAAgC,EAChC,KAAa,EACb,KAAoB,EACpB,MAAc;IAEd,uEAAuE;IACvE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM;SACjB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC;SAC5D,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO,KAAK,MAAM,SAAS,KAAK,KAAK,MAAM,OAAO,CAAC;AACrD,CAAC;AAED,SAAS,QAAQ,CACf,KAAoB,EACpB,KAAa,EACb,KAAoB,EACpB,MAAc;IAEd,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,MAAM,IAAI,GACR,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAClD,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,YAAY,IAAI,KAAK,KAAK,OAAO,IAAI,EAAE,CAAC;IACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,MAAM,OAAO,IAAI,OAAO,CAAC;IAC9D,OAAO,GAAG,MAAM,OAAO,IAAI,GAAG,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC;EAC7E,MAAM,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,KAAa,EAAE,KAAoB;IACjE,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,KAAK,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED,SAAS,OAAO,CAAC,KAAoB;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC1B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;AAC1E,CAAC;AAED,SAAS,UAAU,CACjB,OAAe,EACf,SAAkD;IAElD,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IAC9D,MAAM,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACnD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC;IACzC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7C,IAAI,IAAI,CAAC,EAAE,KAAK,OAAO;YAAE,SAAS;QAClC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,KAAK,CAAC,IAAI,CACR,oBAAoB,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,OAAO,OAAO,CAClG,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO;;;EAGP,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACV,CAAC;AACT,CAAC;AAED,SAAS,WAAW,CAAC,KAAoC;IACvD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,eAAe,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
* Googlebot renders JavaScript, but Bing, a Slack unfurl, an LMS preview and
|
|
5
5
|
* every academic indexer read the HTML that came off the wire — so the title,
|
|
6
6
|
* the description and the canonical of a page must already be in it. A site
|
|
7
|
-
* with a server
|
|
7
|
+
* with a server writes them per request; a static one writes one file per
|
|
8
8
|
* address at build time. Both call this, which is pure string work: no
|
|
9
9
|
* `window`, no `node:fs`.
|
|
10
|
+
*
|
|
11
|
+
* The page it is given is the template, which declares where its head goes and
|
|
12
|
+
* carries none of its own, so this only ever writes — see `./template.ts`.
|
|
10
13
|
*/
|
|
11
14
|
import type { EcosystemSite, SiteId } from '../../ecosystem/core/sites.ts';
|
|
12
15
|
import type { DocumentMeta } from './documentMeta.ts';
|
|
@@ -17,11 +20,18 @@ export interface PageMetaOptions {
|
|
|
17
20
|
site: EcosystemSite | SiteId;
|
|
18
21
|
/** Every address it answers, each with its title and description. */
|
|
19
22
|
routes: readonly RouteMeta[];
|
|
20
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The address being written, query string included: a path, or the absolute
|
|
25
|
+
* address an app reads off the page it is on.
|
|
26
|
+
*/
|
|
21
27
|
url: string;
|
|
22
28
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
29
|
+
* Where the site is served, mount path included, e.g.
|
|
30
|
+
* `https://learn.cheminfo.org/surge`. A server passes the one the request
|
|
31
|
+
* arrived on; a build leaves it out and the site's own host is used. Every
|
|
32
|
+
* address written here is composed on it, so the mount is carried by the
|
|
33
|
+
* origin rather than applied a second time. It is an absolute address, or it
|
|
34
|
+
* is refused.
|
|
25
35
|
* @default `https://<the site's host>`
|
|
26
36
|
*/
|
|
27
37
|
origin?: string;
|
|
@@ -34,11 +44,23 @@ export interface PageMetaOptions {
|
|
|
34
44
|
/**
|
|
35
45
|
* Give a page the title, the description and the canonical address of the route
|
|
36
46
|
* it answers, plus the card a link to it unfurls into.
|
|
37
|
-
* @param html - The built
|
|
47
|
+
* @param html - The built template.
|
|
38
48
|
* @param options - Which site, which address, and where it is served from.
|
|
39
|
-
* @returns The page, with its head
|
|
49
|
+
* @returns The page, with its head written for that route.
|
|
50
|
+
* @throws {Error} When the page carries no `<!--cheminfo:head-->`, when the
|
|
51
|
+
* site answers no route, or when it names an origin that is not an absolute
|
|
52
|
+
* address.
|
|
40
53
|
*/
|
|
41
54
|
export declare function injectPageMeta(html: string, options: PageMetaOptions): string;
|
|
55
|
+
/**
|
|
56
|
+
* The head a route is indexed and shared under, for a caller writing more into
|
|
57
|
+
* the same place — a structured-data block, a tracking snippet.
|
|
58
|
+
* @param options - Which site, which address, and where it is served from.
|
|
59
|
+
* @returns The tags, one per line.
|
|
60
|
+
* @throws {Error} When the site answers no route, or names an origin that is
|
|
61
|
+
* not an absolute address.
|
|
62
|
+
*/
|
|
63
|
+
export declare function pageHeadTags(options: PageMetaOptions): string;
|
|
42
64
|
/**
|
|
43
65
|
* What the page on screen is called and where it is indexed, for the app to
|
|
44
66
|
* write after an in-app move.
|
|
@@ -47,14 +69,8 @@ export declare function injectPageMeta(html: string, options: PageMetaOptions):
|
|
|
47
69
|
* click that changes the page cannot disagree with the page a crawler fetched.
|
|
48
70
|
* @param options - Which site, which address, and where it is served from.
|
|
49
71
|
* @returns The title, the description and the canonical of that address.
|
|
72
|
+
* @throws {Error} When the site answers no route, or names an origin that is
|
|
73
|
+
* not an absolute address.
|
|
50
74
|
*/
|
|
51
75
|
export declare function pageDocumentMeta(options: PageMetaOptions): Required<DocumentMeta>;
|
|
52
|
-
/**
|
|
53
|
-
* Put an addition at the end of the head, where a tracking snippet and a
|
|
54
|
-
* structured-data block both belong.
|
|
55
|
-
* @param html - The page.
|
|
56
|
-
* @param addition - The markup to add, taken as written.
|
|
57
|
-
* @returns The page, with the addition before `</head>`.
|
|
58
|
-
*/
|
|
59
|
-
export declare function insertBeforeHeadEnd(html: string, addition: string): string;
|
|
60
76
|
//# sourceMappingURL=pageMeta.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pageMeta.d.ts","sourceRoot":"","sources":["../../../src/seo/core/pageMeta.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pageMeta.d.ts","sourceRoot":"","sources":["../../../src/seo/core/pageMeta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7B,qEAAqE;IACrE,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAmB7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,GACvB,QAAQ,CAAC,YAAY,CAAC,CAQxB"}
|
package/lib/seo/core/pageMeta.js
CHANGED
|
@@ -4,38 +4,57 @@
|
|
|
4
4
|
* Googlebot renders JavaScript, but Bing, a Slack unfurl, an LMS preview and
|
|
5
5
|
* every academic indexer read the HTML that came off the wire — so the title,
|
|
6
6
|
* the description and the canonical of a page must already be in it. A site
|
|
7
|
-
* with a server
|
|
7
|
+
* with a server writes them per request; a static one writes one file per
|
|
8
8
|
* address at build time. Both call this, which is pure string work: no
|
|
9
9
|
* `window`, no `node:fs`.
|
|
10
|
+
*
|
|
11
|
+
* The page it is given is the template, which declares where its head goes and
|
|
12
|
+
* carries none of its own, so this only ever writes — see `./template.ts`.
|
|
10
13
|
*/
|
|
11
|
-
import {
|
|
14
|
+
import { siteDisplayName } from "../../ecosystem/core/lookup.js";
|
|
12
15
|
import { escapeAttribute, escapeText } from "../../share/core/escape.js";
|
|
13
|
-
import { pageMetaFor
|
|
16
|
+
import { pageMetaFor } from "./routes.js";
|
|
17
|
+
import { mountPathOf, originOf, resolveSite } from "./siteFiles.js";
|
|
18
|
+
import { PAGE_HEAD_MARKER, fill } from "./template.js";
|
|
14
19
|
/**
|
|
15
20
|
* Give a page the title, the description and the canonical address of the route
|
|
16
21
|
* it answers, plus the card a link to it unfurls into.
|
|
17
|
-
* @param html - The built
|
|
22
|
+
* @param html - The built template.
|
|
18
23
|
* @param options - Which site, which address, and where it is served from.
|
|
19
|
-
* @returns The page, with its head
|
|
24
|
+
* @returns The page, with its head written for that route.
|
|
25
|
+
* @throws {Error} When the page carries no `<!--cheminfo:head-->`, when the
|
|
26
|
+
* site answers no route, or when it names an origin that is not an absolute
|
|
27
|
+
* address.
|
|
20
28
|
*/
|
|
21
29
|
export function injectPageMeta(html, options) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
return fill(html, PAGE_HEAD_MARKER, pageHeadTags(options));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The head a route is indexed and shared under, for a caller writing more into
|
|
34
|
+
* the same place — a structured-data block, a tracking snippet.
|
|
35
|
+
* @param options - Which site, which address, and where it is served from.
|
|
36
|
+
* @returns The tags, one per line.
|
|
37
|
+
* @throws {Error} When the site answers no route, or names an origin that is
|
|
38
|
+
* not an absolute address.
|
|
39
|
+
*/
|
|
40
|
+
export function pageHeadTags(options) {
|
|
41
|
+
const name = siteDisplayName(resolveSite(options.site));
|
|
42
|
+
const description = routeMetaOf(options).description;
|
|
43
|
+
const origin = originOf(options);
|
|
26
44
|
const { title, canonical } = pageDocumentMeta(options);
|
|
27
45
|
const image = absolute(options.image ?? '/og.png', origin);
|
|
28
|
-
|
|
46
|
+
return [
|
|
47
|
+
`<title>${escapeText(title)}</title>`,
|
|
48
|
+
`<meta name="description" content="${escapeAttribute(description)}" />`,
|
|
29
49
|
`<link rel="canonical" href="${escapeAttribute(canonical)}" />`,
|
|
30
50
|
'<meta property="og:type" content="website" />',
|
|
31
51
|
`<meta property="og:site_name" content="${escapeAttribute(name)}" />`,
|
|
32
52
|
`<meta property="og:title" content="${escapeAttribute(title)}" />`,
|
|
33
|
-
`<meta property="og:description" content="${escapeAttribute(
|
|
53
|
+
`<meta property="og:description" content="${escapeAttribute(description)}" />`,
|
|
34
54
|
`<meta property="og:url" content="${escapeAttribute(canonical)}" />`,
|
|
35
55
|
`<meta property="og:image" content="${escapeAttribute(image)}" />`,
|
|
36
56
|
'<meta name="twitter:card" content="summary_large_image" />',
|
|
37
57
|
].join('\n');
|
|
38
|
-
return insertBeforeHeadEnd(replaceDescription(replaceTitle(html, title), meta.description), head);
|
|
39
58
|
}
|
|
40
59
|
/**
|
|
41
60
|
* What the page on screen is called and where it is indexed, for the app to
|
|
@@ -45,47 +64,25 @@ export function injectPageMeta(html, options) {
|
|
|
45
64
|
* click that changes the page cannot disagree with the page a crawler fetched.
|
|
46
65
|
* @param options - Which site, which address, and where it is served from.
|
|
47
66
|
* @returns The title, the description and the canonical of that address.
|
|
67
|
+
* @throws {Error} When the site answers no route, or names an origin that is
|
|
68
|
+
* not an absolute address.
|
|
48
69
|
*/
|
|
49
70
|
export function pageDocumentMeta(options) {
|
|
50
71
|
const site = resolveSite(options.site);
|
|
51
|
-
const meta =
|
|
52
|
-
const origin = trimTrailingSlash(options.origin ?? `https://${site.host}`);
|
|
72
|
+
const meta = routeMetaOf(options);
|
|
53
73
|
return {
|
|
54
74
|
title: `${meta.title} — ${siteDisplayName(site)}`,
|
|
55
75
|
description: meta.description,
|
|
56
|
-
canonical: `${
|
|
76
|
+
canonical: `${originOf(options)}${meta.path}`,
|
|
57
77
|
};
|
|
58
78
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
* @returns The page, with the addition before `</head>`.
|
|
65
|
-
*/
|
|
66
|
-
export function insertBeforeHeadEnd(html, addition) {
|
|
67
|
-
const head = html.lastIndexOf('</head>');
|
|
68
|
-
if (head === -1)
|
|
69
|
-
return `${html}\n${addition}\n`;
|
|
70
|
-
return `${html.slice(0, head)}${addition}\n${html.slice(head)}`;
|
|
71
|
-
}
|
|
72
|
-
function resolveSite(site) {
|
|
73
|
-
return typeof site === 'string' ? siteById(site) : site;
|
|
79
|
+
// A server behind a mount is handed the address the browser asked for, and the
|
|
80
|
+
// route table is written from the site's own root, so the mount the origin
|
|
81
|
+
// carries is taken off it before the table is read.
|
|
82
|
+
function routeMetaOf(options) {
|
|
83
|
+
return pageMetaFor(options.routes, options.url, mountPathOf(options));
|
|
74
84
|
}
|
|
75
85
|
function absolute(target, origin) {
|
|
76
86
|
return target.startsWith('/') ? `${origin}${target}` : target;
|
|
77
87
|
}
|
|
78
|
-
function replaceTitle(html, title) {
|
|
79
|
-
const replacement = `<title>${escapeText(title)}</title>`;
|
|
80
|
-
return html.includes('<title>')
|
|
81
|
-
? html.replace(/<title>[\s\S]*?<\/title>/, replacement)
|
|
82
|
-
: insertBeforeHeadEnd(html, replacement);
|
|
83
|
-
}
|
|
84
|
-
function replaceDescription(html, description) {
|
|
85
|
-
const replacement = `<meta name="description" content="${escapeAttribute(description)}" />`;
|
|
86
|
-
const existing = /<meta[^>]*name="description"[^>]*>/;
|
|
87
|
-
return existing.test(html)
|
|
88
|
-
? html.replace(existing, replacement)
|
|
89
|
-
: insertBeforeHeadEnd(html, replacement);
|
|
90
|
-
}
|
|
91
88
|
//# sourceMappingURL=pageMeta.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pageMeta.js","sourceRoot":"","sources":["../../../src/seo/core/pageMeta.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pageMeta.js","sourceRoot":"","sources":["../../../src/seo/core/pageMeta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAIzE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AA8BvD;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,OAAwB;IACnE,OAAO,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,OAAwB;IACnD,MAAM,IAAI,GAAG,eAAe,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC;IACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,IAAI,SAAS,EAAE,MAAM,CAAC,CAAC;IAE3D,OAAO;QACL,UAAU,UAAU,CAAC,KAAK,CAAC,UAAU;QACrC,qCAAqC,eAAe,CAAC,WAAW,CAAC,MAAM;QACvE,+BAA+B,eAAe,CAAC,SAAS,CAAC,MAAM;QAC/D,+CAA+C;QAC/C,0CAA0C,eAAe,CAAC,IAAI,CAAC,MAAM;QACrE,sCAAsC,eAAe,CAAC,KAAK,CAAC,MAAM;QAClE,4CAA4C,eAAe,CAAC,WAAW,CAAC,MAAM;QAC9E,oCAAoC,eAAe,CAAC,SAAS,CAAC,MAAM;QACpE,sCAAsC,eAAe,CAAC,KAAK,CAAC,MAAM;QAClE,4DAA4D;KAC7D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAwB;IAExB,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO;QACL,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,MAAM,eAAe,CAAC,IAAI,CAAC,EAAE;QACjD,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,SAAS,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE;KAC9C,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,2EAA2E;AAC3E,oDAAoD;AACpD,SAAS,WAAW,CAAC,OAAwB;IAC3C,OAAO,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,QAAQ,CAAC,MAAc,EAAE,MAAc;IAC9C,OAAO,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The crawl policy.
|
|
3
|
+
*
|
|
4
|
+
* Our tools are meant to be found, so only the endpoints are kept out of the
|
|
5
|
+
* index — an API prefix and its documentation are not pages — and each one may
|
|
6
|
+
* say in a comment why, because a policy nobody can read is a policy nobody
|
|
7
|
+
* maintains.
|
|
8
|
+
*
|
|
9
|
+
* Every line of this file is a directive, so nothing an author writes may
|
|
10
|
+
* become one they did not: a path is a single line that says something, and
|
|
11
|
+
* carries no fragment, or it is refused; and a comment is folded onto the one
|
|
12
|
+
* line it is written as.
|
|
13
|
+
*/
|
|
14
|
+
import type { SiteFilesOptions } from './siteFiles.ts';
|
|
15
|
+
/** One address kept out of the index, and why. */
|
|
16
|
+
export interface RobotsDisallow {
|
|
17
|
+
/**
|
|
18
|
+
* The address prefix, from the site's own root, e.g. `/v1/`. It is a path,
|
|
19
|
+
* on one line, saying something, carrying no `#`, and padded by nothing: a
|
|
20
|
+
* blank one would read as `Disallow: /` and keep the whole site out of the
|
|
21
|
+
* index — RFC 9309 eats the trailing whitespace of a line, so `" "` is read
|
|
22
|
+
* as nothing at all — one carrying a line break would write whatever follows
|
|
23
|
+
* it as a directive of its own, everything from a `#` onwards is read as a
|
|
24
|
+
* comment, which truncates the path silently, and a padded one is not the
|
|
25
|
+
* address it looks like: `" /v1/"` does not start at the site root, so it is
|
|
26
|
+
* written `Disallow: / /v1/` and matches no address at all, leaving the
|
|
27
|
+
* endpoint crawled by the very line meant to keep it out.
|
|
28
|
+
*/
|
|
29
|
+
path: string;
|
|
30
|
+
/**
|
|
31
|
+
* One sentence written as a `#` line above the directive. The `#` is added
|
|
32
|
+
* when it is not already there, every run of whitespace is folded to a single
|
|
33
|
+
* space so the sentence stays on its own line, and a blank one is written as
|
|
34
|
+
* no line at all.
|
|
35
|
+
* @default undefined — the directive is written on its own
|
|
36
|
+
*/
|
|
37
|
+
comment?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The crawl policy of the address the site is served at.
|
|
41
|
+
*
|
|
42
|
+
* Every path is written under the mount, so a build published as one tool among
|
|
43
|
+
* several on a shared host allows what it actually answers rather than claiming
|
|
44
|
+
* the whole host. The sitemap is named only because this module also writes it:
|
|
45
|
+
* a `Sitemap:` line pointing at a 404 is reported as an error on every fetch.
|
|
46
|
+
* @param options - The site, its routes, and where it is served.
|
|
47
|
+
* @param disallow - Addresses to keep out of the index, each optionally with
|
|
48
|
+
* the sentence saying why.
|
|
49
|
+
* @returns The `robots.txt` document.
|
|
50
|
+
* @throws {Error} When a disallowed address is blank, spans more than one line,
|
|
51
|
+
* carries a `#` or is padded with whitespace, or when the deployment named an
|
|
52
|
+
* origin that is not an absolute address.
|
|
53
|
+
*/
|
|
54
|
+
export declare function robotsTxt(options: SiteFilesOptions, disallow?: ReadonlyArray<string | RobotsDisallow>): string;
|
|
55
|
+
//# sourceMappingURL=robots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"robots.d.ts","sourceRoot":"","sources":["../../../src/seo/core/robots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,kDAAkD;AAClD,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;OAWG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,gBAAgB,EACzB,QAAQ,GAAE,aAAa,CAAC,MAAM,GAAG,cAAc,CAAM,GACpD,MAAM,CAcR"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The crawl policy.
|
|
3
|
+
*
|
|
4
|
+
* Our tools are meant to be found, so only the endpoints are kept out of the
|
|
5
|
+
* index — an API prefix and its documentation are not pages — and each one may
|
|
6
|
+
* say in a comment why, because a policy nobody can read is a policy nobody
|
|
7
|
+
* maintains.
|
|
8
|
+
*
|
|
9
|
+
* Every line of this file is a directive, so nothing an author writes may
|
|
10
|
+
* become one they did not: a path is a single line that says something, and
|
|
11
|
+
* carries no fragment, or it is refused; and a comment is folded onto the one
|
|
12
|
+
* line it is written as.
|
|
13
|
+
*/
|
|
14
|
+
import { joinBasePath } from "../../router/core/basePath.js";
|
|
15
|
+
import { mountPathOf, originOf } from "./siteFiles.js";
|
|
16
|
+
const NEWLINE = /[\n\r]/;
|
|
17
|
+
const WHITESPACE = /\s+/g;
|
|
18
|
+
/**
|
|
19
|
+
* The crawl policy of the address the site is served at.
|
|
20
|
+
*
|
|
21
|
+
* Every path is written under the mount, so a build published as one tool among
|
|
22
|
+
* several on a shared host allows what it actually answers rather than claiming
|
|
23
|
+
* the whole host. The sitemap is named only because this module also writes it:
|
|
24
|
+
* a `Sitemap:` line pointing at a 404 is reported as an error on every fetch.
|
|
25
|
+
* @param options - The site, its routes, and where it is served.
|
|
26
|
+
* @param disallow - Addresses to keep out of the index, each optionally with
|
|
27
|
+
* the sentence saying why.
|
|
28
|
+
* @returns The `robots.txt` document.
|
|
29
|
+
* @throws {Error} When a disallowed address is blank, spans more than one line,
|
|
30
|
+
* carries a `#` or is padded with whitespace, or when the deployment named an
|
|
31
|
+
* origin that is not an absolute address.
|
|
32
|
+
*/
|
|
33
|
+
export function robotsTxt(options, disallow = []) {
|
|
34
|
+
const mount = mountPathOf(options);
|
|
35
|
+
const lines = ['User-agent: *', `Allow: ${joinBasePath(mount, '/')}`];
|
|
36
|
+
for (const entry of disallow) {
|
|
37
|
+
const rule = typeof entry === 'string' ? { path: entry } : entry;
|
|
38
|
+
const comment = rule.comment === undefined ? undefined : commentLine(rule.comment);
|
|
39
|
+
if (comment !== undefined)
|
|
40
|
+
lines.push(comment);
|
|
41
|
+
lines.push(`Disallow: ${joinBasePath(mount, disallowPath(rule.path))}`);
|
|
42
|
+
}
|
|
43
|
+
lines.push('', `Sitemap: ${originOf(options)}/sitemap.xml`, '');
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
function disallowPath(path) {
|
|
47
|
+
if (path === '') {
|
|
48
|
+
throw new Error('a disallowed address is a path, never the empty string');
|
|
49
|
+
}
|
|
50
|
+
if (path.trim() === '') {
|
|
51
|
+
throw new Error(`a disallowed address is a path, never blank: ${JSON.stringify(path)}`);
|
|
52
|
+
}
|
|
53
|
+
if (NEWLINE.test(path)) {
|
|
54
|
+
throw new Error(`a disallowed address is written on one line: ${JSON.stringify(path)}`);
|
|
55
|
+
}
|
|
56
|
+
if (path.includes('#')) {
|
|
57
|
+
throw new Error(`a disallowed address carries no fragment: ${JSON.stringify(path)}`);
|
|
58
|
+
}
|
|
59
|
+
if (path !== path.trim()) {
|
|
60
|
+
throw new Error(`a disallowed address is written without padding: ${JSON.stringify(path)}`);
|
|
61
|
+
}
|
|
62
|
+
return path;
|
|
63
|
+
}
|
|
64
|
+
function commentLine(comment) {
|
|
65
|
+
const text = comment.replaceAll(WHITESPACE, ' ').trim();
|
|
66
|
+
if (text === '')
|
|
67
|
+
return undefined;
|
|
68
|
+
return text.startsWith('#') ? text : `# ${text}`;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=robots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"robots.js","sourceRoot":"","sources":["../../../src/seo/core/robots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AA2BvD,MAAM,OAAO,GAAG,QAAQ,CAAC;AAEzB,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CACvB,OAAyB,EACzB,WAAmD,EAAE;IAErD,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,CAAC,eAAe,EAAE,UAAU,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAEtE,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QACjE,MAAM,OAAO,GACX,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrE,IAAI,OAAO,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,KAAK,CAAC,IAAI,CAAC,aAAa,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,YAAY,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAChE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,gDAAgD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CACvE,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,gDAAgD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CACvE,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,6CAA6C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CACpE,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,oDAAoD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAC3E,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAClC,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;AACnD,CAAC"}
|