react-cheminfo 0.4.1 → 0.6.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.
Files changed (99) hide show
  1. package/README.md +182 -34
  2. package/lib/ecosystem/core/sites.d.ts +1 -1
  3. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  4. package/lib/ecosystem/core/sites.js +10 -0
  5. package/lib/ecosystem/core/sites.js.map +1 -1
  6. package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
  7. package/lib/ecosystem/ui/glyphs.js +3 -0
  8. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  9. package/lib/orbital/ui/AtomicOrbitalCanvas.d.ts +5 -0
  10. package/lib/orbital/ui/AtomicOrbitalCanvas.d.ts.map +1 -1
  11. package/lib/orbital/ui/AtomicOrbitalCanvas.js +4 -3
  12. package/lib/orbital/ui/AtomicOrbitalCanvas.js.map +1 -1
  13. package/lib/orbital/ui/AtomicOrbitalViewer.d.ts +5 -0
  14. package/lib/orbital/ui/AtomicOrbitalViewer.d.ts.map +1 -1
  15. package/lib/orbital/ui/AtomicOrbitalViewer.js.map +1 -1
  16. package/lib/orbital/ui/axesGeometry.d.ts +27 -0
  17. package/lib/orbital/ui/axesGeometry.d.ts.map +1 -0
  18. package/lib/orbital/ui/axesGeometry.js +74 -0
  19. package/lib/orbital/ui/axesGeometry.js.map +1 -0
  20. package/lib/orbital/ui/camera.d.ts +7 -0
  21. package/lib/orbital/ui/camera.d.ts.map +1 -1
  22. package/lib/orbital/ui/camera.js +8 -1
  23. package/lib/orbital/ui/camera.js.map +1 -1
  24. package/lib/orbital/ui/renderAxes.d.ts +53 -0
  25. package/lib/orbital/ui/renderAxes.d.ts.map +1 -0
  26. package/lib/orbital/ui/renderAxes.js +110 -0
  27. package/lib/orbital/ui/renderAxes.js.map +1 -0
  28. package/lib/orbital/ui/viewer.d.ts +16 -0
  29. package/lib/orbital/ui/viewer.d.ts.map +1 -1
  30. package/lib/orbital/ui/viewer.js +24 -2
  31. package/lib/orbital/ui/viewer.js.map +1 -1
  32. package/lib/seo/core/documentMeta.d.ts +2 -2
  33. package/lib/seo/core/documentMeta.js +4 -3
  34. package/lib/seo/core/documentMeta.js.map +1 -1
  35. package/lib/seo/core/index.d.ts +13 -4
  36. package/lib/seo/core/index.d.ts.map +1 -1
  37. package/lib/seo/core/index.js +8 -3
  38. package/lib/seo/core/index.js.map +1 -1
  39. package/lib/seo/core/noscript.d.ts +97 -0
  40. package/lib/seo/core/noscript.d.ts.map +1 -0
  41. package/lib/seo/core/noscript.js +93 -0
  42. package/lib/seo/core/noscript.js.map +1 -0
  43. package/lib/seo/core/pageMeta.d.ts +30 -14
  44. package/lib/seo/core/pageMeta.d.ts.map +1 -1
  45. package/lib/seo/core/pageMeta.js +40 -43
  46. package/lib/seo/core/pageMeta.js.map +1 -1
  47. package/lib/seo/core/robots.d.ts +55 -0
  48. package/lib/seo/core/robots.d.ts.map +1 -0
  49. package/lib/seo/core/robots.js +70 -0
  50. package/lib/seo/core/robots.js.map +1 -0
  51. package/lib/seo/core/routes.d.ts +73 -5
  52. package/lib/seo/core/routes.d.ts.map +1 -1
  53. package/lib/seo/core/routes.js +142 -16
  54. package/lib/seo/core/routes.js.map +1 -1
  55. package/lib/seo/core/siteFiles.d.ts +39 -43
  56. package/lib/seo/core/siteFiles.d.ts.map +1 -1
  57. package/lib/seo/core/siteFiles.js +53 -69
  58. package/lib/seo/core/siteFiles.js.map +1 -1
  59. package/lib/seo/core/startDocumentMeta.d.ts +44 -0
  60. package/lib/seo/core/startDocumentMeta.d.ts.map +1 -0
  61. package/lib/seo/core/startDocumentMeta.js +47 -0
  62. package/lib/seo/core/startDocumentMeta.js.map +1 -0
  63. package/lib/seo/core/structuredData.d.ts +48 -0
  64. package/lib/seo/core/structuredData.d.ts.map +1 -0
  65. package/lib/seo/core/structuredData.js +41 -0
  66. package/lib/seo/core/structuredData.js.map +1 -0
  67. package/lib/seo/core/template.d.ts +48 -0
  68. package/lib/seo/core/template.d.ts.map +1 -0
  69. package/lib/seo/core/template.js +53 -0
  70. package/lib/seo/core/template.js.map +1 -0
  71. package/lib/seo/vite/ogCard.d.ts +9 -1
  72. package/lib/seo/vite/ogCard.d.ts.map +1 -1
  73. package/lib/seo/vite/ogCard.js +14 -4
  74. package/lib/seo/vite/ogCard.js.map +1 -1
  75. package/lib/seo/vite/prerender.d.ts +38 -7
  76. package/lib/seo/vite/prerender.d.ts.map +1 -1
  77. package/lib/seo/vite/prerender.js +68 -30
  78. package/lib/seo/vite/prerender.js.map +1 -1
  79. package/package.json +1 -1
  80. package/src/ecosystem/core/sites.ts +11 -0
  81. package/src/ecosystem/ui/glyphs.tsx +19 -0
  82. package/src/orbital/ui/AtomicOrbitalCanvas.tsx +9 -2
  83. package/src/orbital/ui/AtomicOrbitalViewer.tsx +5 -0
  84. package/src/orbital/ui/axesGeometry.ts +91 -0
  85. package/src/orbital/ui/camera.ts +9 -1
  86. package/src/orbital/ui/renderAxes.ts +190 -0
  87. package/src/orbital/ui/viewer.ts +32 -2
  88. package/src/seo/core/documentMeta.ts +5 -5
  89. package/src/seo/core/index.ts +19 -12
  90. package/src/seo/core/noscript.ts +195 -0
  91. package/src/seo/core/pageMeta.ts +54 -53
  92. package/src/seo/core/robots.ts +114 -0
  93. package/src/seo/core/routes.ts +181 -14
  94. package/src/seo/core/siteFiles.ts +58 -96
  95. package/src/seo/core/startDocumentMeta.ts +77 -0
  96. package/src/seo/core/structuredData.ts +80 -0
  97. package/src/seo/core/template.ts +54 -0
  98. package/src/seo/vite/ogCard.ts +15 -5
  99. package/src/seo/vite/prerender.ts +105 -58
@@ -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 rewrites them per request; a static one writes one file per
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
- /** The address being written, query string included. */
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
- * Origin every absolute address is built on. A server passes the one the
24
- * request arrived on; a build leaves it out and the site's own host is used.
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 page.
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 rewritten for that route.
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;;;;;;;;;GASG;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;AAG7C,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7B,qEAAqE;IACrE,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,CAuB7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAAC,YAAY,CAAC,CASjF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1E"}
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"}
@@ -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 rewrites them per request; a static one writes one file per
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 { siteById, siteDisplayName } from "../../ecosystem/core/lookup.js";
14
+ import { siteDisplayName } from "../../ecosystem/core/lookup.js";
12
15
  import { escapeAttribute, escapeText } from "../../share/core/escape.js";
13
- import { pageMetaFor, trimTrailingSlash } from "./routes.js";
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 page.
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 rewritten for that route.
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
- const site = resolveSite(options.site);
23
- const meta = pageMetaFor(options.routes, options.url);
24
- const name = siteDisplayName(site);
25
- const origin = trimTrailingSlash(options.origin ?? `https://${site.host}`);
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
- const head = [
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(meta.description)}" />`,
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 = pageMetaFor(options.routes, options.url);
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: `${origin}${meta.path}`,
76
+ canonical: `${originOf(options)}${meta.path}`,
57
77
  };
58
78
  }
59
- /**
60
- * Put an addition at the end of the head, where a tracking snippet and a
61
- * structured-data block both belong.
62
- * @param html - The page.
63
- * @param addition - The markup to add, taken as written.
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":"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"}
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"}
@@ -15,9 +15,33 @@ export interface RouteMeta {
15
15
  title: string;
16
16
  /** One sentence, in the words someone would search for. */
17
17
  description: string;
18
+ /**
19
+ * The label the page is linked under where a title written for a search
20
+ * result is too long to read as a menu entry — the `noscript` index.
21
+ * @default the route's own title
22
+ */
23
+ short?: string;
24
+ /**
25
+ * What the page is for, written after an em dash next to its link in the
26
+ * `noscript` index.
27
+ * @default undefined — the link stands on its own
28
+ */
29
+ note?: string;
30
+ /**
31
+ * Whether the route also answers every address beneath it, so a section
32
+ * carrying more pages than a table can hold — an entry per structure, per
33
+ * ligand, per identifier — is indexed under the section rather than under the
34
+ * home page. Those addresses are canonical to the section itself.
35
+ * @default false
36
+ */
37
+ prefix?: boolean;
18
38
  }
19
39
  /**
20
40
  * The route an address names.
41
+ *
42
+ * An address a route claims exactly always wins over one that claims it as a
43
+ * subtree, and between two subtrees the longer claim wins, so `/molecules/HEM`
44
+ * is a molecule rather than whatever `/` answers.
21
45
  * @param routes - Every address the site answers.
22
46
  * @param path - Absolute path, without a query string.
23
47
  * @returns Its entry, or `undefined` when the site does not know the address.
@@ -29,13 +53,37 @@ export declare function routeFor(routes: readonly RouteMeta[], path: string): Ro
29
53
  * An address the site does not know is described as the home page rather than
30
54
  * invented on the fly, which is what the router does with it too. The query
31
55
  * string never reaches the answer: the structure being drawn and the
32
- * configuration a shared link carries are not pages of their own.
56
+ * configuration a shared link carries are not pages of their own. An absolute
57
+ * address is read for its path, so an app handing over `location.href` after an
58
+ * in-app move is answered rather than silently described as the home page.
59
+ *
60
+ * The route table is written from the site's own root, and a server behind a
61
+ * mount is handed the address the browser asked for — `/surge/exercises` for a
62
+ * table that names `/exercises`. So the address is read at the site's own root
63
+ * first, and the four lookups run in this order:
64
+ *
65
+ * 1. the mount taken off, claimed exactly;
66
+ * 2. the address as written, claimed exactly;
67
+ * 3. the mount taken off, claimed as a subtree;
68
+ * 4. the address as written, claimed as a subtree.
69
+ *
70
+ * Exact before subtree, or a `prefix` route — a home page answering everything
71
+ * beneath it above all — would claim every mounted address and the mount would
72
+ * never come off. Stripped before as-written, or the mount itself would open
73
+ * whichever page happens to carry the mount's own name rather than the site's
74
+ * front page. Taking the address as written second is what leaves an unmounted
75
+ * caller answering exactly as before, and lets a table whose own paths start
76
+ * with the mount's name still be read.
33
77
  * @param routes - Every address the site answers.
34
- * @param url - The address, query string and fragment included.
78
+ * @param url - The address, query string and fragment included, either as a
79
+ * path or as an absolute `scheme://host/path` address.
80
+ * @param basePath - The path the site is mounted at, when the address carries
81
+ * it, written `surge`, `/surge` or `/surge/`.
82
+ * @default '' — the address is already written from the site's own root
35
83
  * @returns The route it is indexed as.
36
84
  * @throws {Error} When the table is empty, so there is no page to fall back to.
37
85
  */
38
- export declare function pageMetaFor(routes: readonly RouteMeta[], url: string): RouteMeta;
86
+ export declare function pageMetaFor(routes: readonly RouteMeta[], url: string, basePath?: string): RouteMeta;
39
87
  /**
40
88
  * The page an unknown address falls back to.
41
89
  * @param routes - Every address the site answers.
@@ -44,9 +92,29 @@ export declare function pageMetaFor(routes: readonly RouteMeta[], url: string):
44
92
  */
45
93
  export declare function homeRoute(routes: readonly RouteMeta[]): RouteMeta;
46
94
  /**
47
- * Drop a trailing slash, so `/about/` and `/about` are one page.
95
+ * Check a route table before a build reads it as a set of file names.
96
+ *
97
+ * An address written twice ships two sitemap entries and two links to a page
98
+ * only the first entry describes, and one carrying a `..` segment writes its
99
+ * file outside the build output — a real build asked for `/../escaped` and got
100
+ * a sibling of `dist`. Two addresses that differ only in an empty segment or in
101
+ * case are the same defect wearing a disguise: `//x` and `/x` both write
102
+ * `dist/x/index.html`, and so do `/About` and `/about` on the case-insensitive
103
+ * filesystem macOS and Windows ship by default — one file, two sitemap entries,
104
+ * and only one of the two descriptions survives. All of it is author
105
+ * configuration read at build time, so it is refused where it is written rather
106
+ * than repaired where it lands.
107
+ * @param routes - Every address the site answers.
108
+ * @throws {Error} When the table is empty, names one address twice — under any
109
+ * of those spellings — or carries a path that is not one.
110
+ */
111
+ export declare function assertRoutes(routes: readonly RouteMeta[]): void;
112
+ /**
113
+ * Drop the trailing slashes, so `/about/` and `/about` are one page and an
114
+ * origin written `https://host/surge//` composes one address rather than one
115
+ * with an empty segment in it.
48
116
  * @param value - A path or an origin.
49
- * @returns It, without the trailing slash `/` itself keeps.
117
+ * @returns It, without the trailing slashes `/` itself keeps.
50
118
  */
51
119
  export declare function trimTrailingSlash(value: string): string;
52
120
  //# sourceMappingURL=routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/seo/core/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,IAAI,EAAE,MAAM,GACX,SAAS,GAAG,SAAS,CAMvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,GAAG,EAAE,MAAM,GACV,SAAS,CAKX;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,CAIjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/seo/core/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,IAAI,EAAE,MAAM,GACX,SAAS,GAAG,SAAS,CAEvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,GAAG,EAAE,MAAM,EACX,QAAQ,SAAK,GACZ,SAAS,CAWX;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,CAIjE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAwB/D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGvD"}