react-cheminfo 0.35.0 → 0.37.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 (66) hide show
  1. package/bin/check-seo.mjs +333 -0
  2. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  3. package/lib/ecosystem/core/sites.js +17 -0
  4. package/lib/ecosystem/core/sites.js.map +1 -1
  5. package/lib/ecosystem/core/types.d.ts +1 -1
  6. package/lib/ecosystem/core/types.d.ts.map +1 -1
  7. package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
  8. package/lib/ecosystem/ui/glyphs.js +4 -0
  9. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  10. package/lib/language/core/index.d.ts +2 -0
  11. package/lib/language/core/index.d.ts.map +1 -1
  12. package/lib/language/core/index.js +1 -0
  13. package/lib/language/core/index.js.map +1 -1
  14. package/lib/language/core/languagePath.d.ts +52 -0
  15. package/lib/language/core/languagePath.d.ts.map +1 -0
  16. package/lib/language/core/languagePath.js +65 -0
  17. package/lib/language/core/languagePath.js.map +1 -0
  18. package/lib/seo/core/alternates.d.ts +40 -0
  19. package/lib/seo/core/alternates.d.ts.map +1 -0
  20. package/lib/seo/core/alternates.js +42 -0
  21. package/lib/seo/core/alternates.js.map +1 -0
  22. package/lib/seo/core/index.d.ts +3 -0
  23. package/lib/seo/core/index.d.ts.map +1 -1
  24. package/lib/seo/core/index.js +2 -0
  25. package/lib/seo/core/index.js.map +1 -1
  26. package/lib/seo/core/noscript.d.ts +9 -0
  27. package/lib/seo/core/noscript.d.ts.map +1 -1
  28. package/lib/seo/core/noscript.js +7 -2
  29. package/lib/seo/core/noscript.js.map +1 -1
  30. package/lib/seo/core/pageMeta.d.ts +23 -0
  31. package/lib/seo/core/pageMeta.d.ts.map +1 -1
  32. package/lib/seo/core/pageMeta.js +52 -3
  33. package/lib/seo/core/pageMeta.js.map +1 -1
  34. package/lib/seo/core/pageProse.d.ts +76 -0
  35. package/lib/seo/core/pageProse.d.ts.map +1 -0
  36. package/lib/seo/core/pageProse.js +92 -0
  37. package/lib/seo/core/pageProse.js.map +1 -0
  38. package/lib/seo/core/routeProblems.d.ts +33 -0
  39. package/lib/seo/core/routeProblems.d.ts.map +1 -0
  40. package/lib/seo/core/routeProblems.js +89 -0
  41. package/lib/seo/core/routeProblems.js.map +1 -0
  42. package/lib/seo/core/routes.d.ts +14 -0
  43. package/lib/seo/core/routes.d.ts.map +1 -1
  44. package/lib/seo/core/routes.js.map +1 -1
  45. package/lib/seo/core/siteFiles.d.ts.map +1 -1
  46. package/lib/seo/core/siteFiles.js +2 -1
  47. package/lib/seo/core/siteFiles.js.map +1 -1
  48. package/lib/seo/vite/prerender.d.ts +13 -0
  49. package/lib/seo/vite/prerender.d.ts.map +1 -1
  50. package/lib/seo/vite/prerender.js +17 -10
  51. package/lib/seo/vite/prerender.js.map +1 -1
  52. package/package.json +2 -1
  53. package/src/ecosystem/core/sites.ts +17 -0
  54. package/src/ecosystem/core/types.ts +1 -0
  55. package/src/ecosystem/ui/glyphs.tsx +16 -0
  56. package/src/language/core/index.ts +2 -0
  57. package/src/language/core/languagePath.ts +78 -0
  58. package/src/seo/core/alternates.ts +65 -0
  59. package/src/seo/core/index.ts +3 -0
  60. package/src/seo/core/noscript.ts +18 -2
  61. package/src/seo/core/pageMeta.ts +74 -3
  62. package/src/seo/core/pageProse.ts +146 -0
  63. package/src/seo/core/routeProblems.ts +111 -0
  64. package/src/seo/core/routes.ts +14 -0
  65. package/src/seo/core/siteFiles.ts +2 -1
  66. package/src/seo/vite/prerender.ts +30 -11
@@ -0,0 +1,89 @@
1
+ /**
2
+ * What is wrong with the prose a site is indexed under.
3
+ *
4
+ * A route table is the only place a search result is written, and it is written
5
+ * once and read for years, so the limits that decide whether a result is
6
+ * readable are checked rather than trusted: a title Google cuts in half, a
7
+ * description too short to say anything or long enough to be clipped, two pages
8
+ * sharing a sentence — which is how a site asks to be deduplicated down to one
9
+ * result — and a snippet promising something the page does not carry.
10
+ *
11
+ * The paths are `assertRoutes`' business; this is about the words.
12
+ */
13
+ /** The longest title that survives a search result whole. */
14
+ const TITLE_LIMIT = 60;
15
+ /** The shortest description that says anything. */
16
+ const DESCRIPTION_MIN = 110;
17
+ /** The longest description a result shows without clipping it. */
18
+ const DESCRIPTION_MAX = 160;
19
+ /**
20
+ * What a site never says about itself, in the words it would say it in. A
21
+ * snippet naming a repository, a tracker or a licence is both a promise the
22
+ * page does not keep and a thing we do not publish.
23
+ */
24
+ const WITHHELD = /\b(?:licence|license|licensing|open[ -]source|repositor(?:y|ies)|issue tracker|source code|report a (?:problem|bug|issue))\b/i;
25
+ /**
26
+ * The phrase in a snippet that names something a site does not publish.
27
+ *
28
+ * Shared with the build-time checker, which reads the same sentences back out
29
+ * of the pages they were written into: one list of words, checked where they
30
+ * are authored and again where they landed.
31
+ * @param text - A title or a description.
32
+ * @returns The phrase, or `undefined` when there is none.
33
+ */
34
+ export function withheldPhrase(text) {
35
+ return WITHHELD.exec(text)?.[0];
36
+ }
37
+ /**
38
+ * Check the table a site is indexed under, before a build reads it.
39
+ *
40
+ * Returns the problems rather than throwing, so a site asserts an empty list in
41
+ * its own test and reads every one of them at once.
42
+ * @param routes - Every address the site answers.
43
+ * @returns One line per problem, empty when the table is fit to ship.
44
+ */
45
+ export function routeProblems(routes) {
46
+ const problems = [];
47
+ if (routes.length === 0) {
48
+ problems.push('the table is empty: a site answers at least one route.');
49
+ return problems;
50
+ }
51
+ const titles = new Map();
52
+ const descriptions = new Map();
53
+ for (const route of routes) {
54
+ const at = route.path;
55
+ if (route.title.trim() === '') {
56
+ problems.push(`${at}: the title is empty.`);
57
+ }
58
+ else if (route.title.length > TITLE_LIMIT) {
59
+ problems.push(`${at}: the title is ${route.title.length} characters, and the site name is appended to it — at most ${TITLE_LIMIT} survives a result whole.`);
60
+ }
61
+ const length = route.description.length;
62
+ if (length < DESCRIPTION_MIN) {
63
+ problems.push(`${at}: the description is ${length} characters — at least ${DESCRIPTION_MIN}, or the result says half of what the page is.`);
64
+ }
65
+ else if (length > DESCRIPTION_MAX) {
66
+ problems.push(`${at}: the description is ${length} characters — at most ${DESCRIPTION_MAX}, or the sentence is cut off in the result itself.`);
67
+ }
68
+ const titleFirst = titles.get(route.title);
69
+ if (titleFirst === undefined) {
70
+ titles.set(route.title, at);
71
+ }
72
+ else {
73
+ problems.push(`${at}: the title repeats the one at ${titleFirst}.`);
74
+ }
75
+ const descriptionFirst = descriptions.get(route.description);
76
+ if (descriptionFirst === undefined) {
77
+ descriptions.set(route.description, at);
78
+ }
79
+ else {
80
+ problems.push(`${at}: the description repeats the one at ${descriptionFirst}.`);
81
+ }
82
+ const withheld = withheldPhrase(route.title) ?? withheldPhrase(route.description);
83
+ if (withheld !== undefined) {
84
+ problems.push(`${at}: the snippet says "${withheld}" — a site names no repository, tracker or licence of ours.`);
85
+ }
86
+ }
87
+ return problems;
88
+ }
89
+ //# sourceMappingURL=routeProblems.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeProblems.js","sourceRoot":"","sources":["../../../src/seo/core/routeProblems.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,20 @@ export interface RouteMeta {
27
27
  * @default undefined — the link stands on its own
28
28
  */
29
29
  note?: string;
30
+ /**
31
+ * Whether a search engine is meant to list the page.
32
+ *
33
+ * A maintenance screen — a curation queue, an import run, an admin table — is
34
+ * a real address a signed-in person opens, so it belongs in the table the
35
+ * router and the tab title read. It is not a result anybody wants: it says
36
+ * nothing a visitor searched for, and a chemist who lands on it has been sent
37
+ * to the wrong place. Such a route is left out of the sitemap and the page
38
+ * answers `noindex`, which is how a page is kept out of the index — never a
39
+ * `Disallow`, which only stops the crawl and still lets the address be listed
40
+ * from a link somewhere else.
41
+ * @default true
42
+ */
43
+ indexed?: boolean;
30
44
  /**
31
45
  * Whether the route also answers every address beneath it, so a section
32
46
  * carrying more pages than a table can hold — an entry per structure, per
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/seo/core/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgBH,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;;;;;;;;;;GAUG;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;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAwB/D"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/seo/core/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgBH,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;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;GAUG;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;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAwB/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/seo/core/routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/seo/core/routes.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"siteFiles.d.ts","sourceRoot":"","sources":["../../../src/seo/core/siteFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAKxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,gCAAgC;IAChC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAgB5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,CAEjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAQ1D;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAE7D"}
1
+ {"version":3,"file":"siteFiles.d.ts","sourceRoot":"","sources":["../../../src/seo/core/siteFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAKxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,gCAAgC;IAChC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAiB5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,CAEjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAQ1D;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAE7D"}
@@ -29,10 +29,11 @@ const HTTP_ORIGIN = /^https?:\/\//i;
29
29
  */
30
30
  export function sitemapXml(options) {
31
31
  const origin = originOf(options);
32
- if (options.routes.length === 0) {
32
+ if (options.routes.every((route) => route.indexed === false)) {
33
33
  throw new Error('a sitemap lists at least one address');
34
34
  }
35
35
  const entries = options.routes
36
+ .filter((route) => route.indexed !== false)
36
37
  .map((route) => ` <url><loc>${escapeText(`${origin}${route.path}`)}</loc></url>`)
37
38
  .join('\n');
38
39
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -1 +1 @@
1
- {"version":3,"file":"siteFiles.js","sourceRoot":"","sources":["../../../src/seo/core/siteFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAIxD,+EAA+E;AAC/E,kFAAkF;AAClF,kEAAkE;AAClE,MAAM,WAAW,GAAG,eAAe,CAAC;AAiBpC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,OAAyB;IAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM;SAC3B,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CACR,eAAe,UAAU,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,cAAc,CACpE;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO;;EAEP,OAAO;;CAER,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAyB;IACnD,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAyB;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,sEAAsE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAyB;IACnD,OAAO,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"siteFiles.js","sourceRoot":"","sources":["../../../src/seo/core/siteFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAIxD,+EAA+E;AAC/E,kFAAkF;AAClF,kEAAkE;AAClE,MAAM,WAAW,GAAG,eAAe,CAAC;AAiBpC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,OAAyB;IAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM;SAC3B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC;SAC1C,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CACR,eAAe,UAAU,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,cAAc,CACpE;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO;;EAEP,OAAO;;CAER,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAyB;IACnD,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAyB;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,sEAAsE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAyB;IACnD,OAAO,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACvC,CAAC"}
@@ -19,6 +19,7 @@
19
19
  import type { Plugin } from 'vite';
20
20
  import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
21
21
  import type { NoscriptText } from '../core/noscript.ts';
22
+ import type { PageContent } from '../core/pageProse.ts';
22
23
  import type { RobotsDisallow } from '../core/robots.ts';
23
24
  import type { RouteMeta } from '../core/routes.ts';
24
25
  /** What the build needs to know to write the site's addresses. */
@@ -77,6 +78,18 @@ export interface PrerenderOptions {
77
78
  * @default true
78
79
  */
79
80
  noscript?: boolean | NoscriptText;
81
+ /**
82
+ * What each page says for itself, above the crawl path: its own heading, its
83
+ * prose and the facts it would show anyway, read from the same data the app
84
+ * renders from.
85
+ *
86
+ * Without it every address ships the same body — the site's menu — and a
87
+ * search engine handed a hundred identical bodies keeps one of them. It is a
88
+ * function of the route rather than a field of it, so the prose stays out of
89
+ * the bundle the browser downloads: only the build ever calls it.
90
+ * @default undefined — every page carries the menu alone
91
+ */
92
+ content?: (route: RouteMeta) => PageContent | undefined;
80
93
  }
81
94
  /**
82
95
  * Prerender every routed address of a cheminfo site.
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../../src/seo/vite/prerender.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAMnD,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,qEAAqE;IACrE,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IACxD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CA6EnE"}
1
+ {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../../src/seo/vite/prerender.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAMnD,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,qEAAqE;IACrE,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IACxD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;IAClC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,WAAW,GAAG,SAAS,CAAC;CACzD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CA4EnE"}
@@ -37,12 +37,12 @@ export function cheminfoPrerender(options) {
37
37
  const { site, routes, origin, robots = [] } = options;
38
38
  assertRoutes(routes);
39
39
  const structuredData = structuredDataOf(options);
40
- const crawlPath = crawlPathOf(options);
41
40
  let out = 'dist';
42
41
  let serve = false;
43
42
  let logger = null;
44
- const page = (template, url) => {
45
- const head = fill(template, PAGE_HEAD_MARKER, `${pageHeadTags({ site, routes, origin, url })}${structuredData}`);
43
+ const page = (template, route) => {
44
+ const head = fill(template, PAGE_HEAD_MARKER, `${pageHeadTags({ site, routes, origin, url: route.path })}${structuredData}`);
45
+ const crawlPath = crawlPathOf(options, route);
46
46
  return crawlPath === '' ? head : fill(head, PAGE_BODY_MARKER, crawlPath);
47
47
  };
48
48
  return {
@@ -56,22 +56,22 @@ export function cheminfoPrerender(options) {
56
56
  // from the home route rather than shipped with its markers showing.
57
57
  transformIndexHtml: {
58
58
  order: 'post',
59
- handler: (html) => serve ? page(html, homeRoute(routes).path) : html,
59
+ handler: (html) => (serve ? page(html, homeRoute(routes)) : html),
60
60
  },
61
61
  closeBundle() {
62
62
  if (serve)
63
63
  return;
64
64
  const template = readFileSync(join(out, 'index.html'), 'utf8');
65
- const write = (url, file) => {
65
+ const write = (route, file) => {
66
66
  mkdirSync(dirname(file), { recursive: true });
67
- writeFileSync(file, page(template, url));
67
+ writeFileSync(file, page(template, route));
68
68
  };
69
69
  let root = false;
70
70
  for (const route of routes) {
71
71
  const address = trimTrailingSlash(route.path);
72
72
  if (address === '/')
73
73
  root = true;
74
- write(route.path, address === '/'
74
+ write(route, address === '/'
75
75
  ? join(out, 'index.html')
76
76
  : join(out, address.slice(1), 'index.html'));
77
77
  }
@@ -79,7 +79,7 @@ export function cheminfoPrerender(options) {
79
79
  // no root would otherwise leave the template vite built, and ship a site
80
80
  // whose front page carries its markers instead of a head.
81
81
  if (!root)
82
- write(homeRoute(routes).path, join(out, 'index.html'));
82
+ write(homeRoute(routes), join(out, 'index.html'));
83
83
  writeFileSync(join(out, 'sitemap.xml'), sitemapXml({ site, routes, origin }));
84
84
  if (robots !== false) {
85
85
  writeFileSync(join(out, 'robots.txt'), robotsTxt({ site, routes, origin }, robots));
@@ -105,11 +105,18 @@ function structuredDataOf(options) {
105
105
  currency,
106
106
  })}`;
107
107
  }
108
- function crawlPathOf(options) {
108
+ function crawlPathOf(options, route) {
109
109
  const { site, routes, origin, noscript = true } = options;
110
110
  if (noscript === false)
111
111
  return '';
112
+ const content = options.content?.(route);
112
113
  const { routes: listed, ...prose } = noscript === true ? {} : noscript;
113
- return noscriptIndex({ site, origin, ...prose, routes: listed ?? routes });
114
+ return noscriptIndex({
115
+ site,
116
+ origin,
117
+ ...prose,
118
+ routes: listed ?? routes,
119
+ content,
120
+ });
114
121
  }
115
122
  //# sourceMappingURL=prerender.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.js","sourceRoot":"","sources":["../../../src/seo/vite/prerender.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prerender.js","sourceRoot":"","sources":["../../../src/seo/vite/prerender.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-cheminfo",
3
- "version": "0.35.0",
3
+ "version": "0.37.0",
4
4
  "license": "MIT",
5
5
  "description": "React components, hooks and framework-free helpers shared by the cheminfo sites: site chrome, citations, sharing, routing, pedagogy, charts, and the structure, orbital and 3D molecule viewers",
6
6
  "author": "Luc Patiny",
@@ -33,6 +33,7 @@
33
33
  "bin": {
34
34
  "cheminfo-check-deploy": "bin/check-deploy.mjs",
35
35
  "cheminfo-check-messages": "bin/check-messages.mjs",
36
+ "cheminfo-check-seo": "bin/check-seo.mjs",
36
37
  "cheminfo-check-tokens": "bin/check-tokens.mjs"
37
38
  },
38
39
  "files": [
@@ -56,6 +56,23 @@ export const ECOSYSTEM_SITES: readonly EcosystemSite[] = [
56
56
  brandAlt: '#9a3412',
57
57
  mark: { plate: '#1c6e42', accent: '#ea580c' },
58
58
  },
59
+ {
60
+ id: 'iupac-name',
61
+ // The name splits on its own hyphen, so it carries no domain and no dot,
62
+ // and the hyphen stays with the first half so the address reads as typed.
63
+ name: { lead: 'iupac-', alt: 'name' },
64
+ host: 'iupac-name.cheminfo.org',
65
+ tagline: 'The IUPAC name of a structure, and the structure a name denotes.',
66
+ group: 'structures',
67
+ // The blue of the IUPAC Blue Book, whose 2013 recommendations are what the
68
+ // site names by. A violet would repeat the lead inchi already owns.
69
+ brand: '#1e40af',
70
+ // The amber the mark's locant 1 is drawn in sits at 3.2:1 on white, short
71
+ // of the 4.5:1 of body text, so the second half of the wordmark takes this
72
+ // darkened form instead, at 7.1:1.
73
+ brandAlt: '#92400e',
74
+ mark: { plate: '#1e40af', accent: '#d97706' },
75
+ },
59
76
  {
60
77
  id: 'openbabel',
61
78
  name: { lead: 'openbabel', alt: 'cheminfo', dot: true },
@@ -6,6 +6,7 @@ export type SiteId =
6
6
  | 'inchi'
7
7
  | 'vcl'
8
8
  | 'smiles'
9
+ | 'iupac-name'
9
10
  | 'openbabel'
10
11
  | 'chemcalc'
11
12
  | 'dbe'
@@ -106,6 +106,22 @@ export const GLYPHS: Record<SiteId, (alt: string) => ReactNode> = {
106
106
  />
107
107
  </>
108
108
  ),
109
+ // A parent chain numbered from the end nearest its group: the atom that
110
+ // takes locant 1 carries the colour, because the numbering is what a name
111
+ // is built on.
112
+ 'iupac-name': (alt) => (
113
+ <>
114
+ <path
115
+ d="M8 20.5 14 10l6 10.5L26 10"
116
+ fill="none"
117
+ stroke="#ffffff"
118
+ strokeWidth="2.6"
119
+ strokeLinecap="round"
120
+ strokeLinejoin="round"
121
+ />
122
+ <circle cx="8" cy="20.5" r="3.4" fill={alt} />
123
+ </>
124
+ ),
109
125
  // Two arrows crossing in opposite directions: one notation turned into
110
126
  // another and back, which is the whole of what this one does.
111
127
  openbabel: (alt) => (
@@ -1,4 +1,6 @@
1
1
  export { languageName } from './languageName.ts';
2
+ export type { LanguagePath } from './languagePath.ts';
3
+ export { readLanguagePath, withLanguagePath } from './languagePath.ts';
2
4
  export {
3
5
  LANGUAGE_PARAM,
4
6
  isLanguageTag,
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The language a translated site routes by: a prefix on its own addresses.
3
+ *
4
+ * `/fr/tutorial` is the French tutorial, and English is written with no prefix
5
+ * at all, so every link already handed out in a course stays alive and the site
6
+ * does not acquire a second spelling of its own front page.
7
+ *
8
+ * This is the half of the design that makes a translation *findable*. A site
9
+ * that keeps its language in a query parameter, in the hash or in
10
+ * `localStorage` serves five languages at one address: a crawler sees one page,
11
+ * indexes one language, and the other four may as well not have been written.
12
+ * The parameter stays — it is how the language travels to a sibling that may
13
+ * not speak it (`./languageParam.ts`) — but it is not how a translated site
14
+ * routes.
15
+ *
16
+ * The prefix sits between the mount and the route, so a tool served as one of
17
+ * several on a shared host reads `/surge/fr/exercises`: the mount comes off
18
+ * first (`router/core/basePath.ts`), then the language, then the table is read.
19
+ */
20
+
21
+ import type { Language } from '../../i18n/core/languages.ts';
22
+ import { DEFAULT_LANGUAGE } from '../../i18n/core/languages.ts';
23
+ import { trimTrailingSlash } from '../../router/core/address.ts';
24
+
25
+ /** An address split into the language it names and the page it opens. */
26
+ export interface LanguagePath {
27
+ /** The language named, or the default one when the address names none. */
28
+ language: Language;
29
+ /** The address from the site's own root, with the prefix taken off. */
30
+ path: string;
31
+ }
32
+
33
+ /**
34
+ * Read the language prefix off one of the site's own addresses.
35
+ *
36
+ * Only a language the site actually speaks is read as a prefix: on a site with
37
+ * no Italian, `/it` is whatever page is called `it`, not an empty Italian front
38
+ * page. So the list is the site's, never the family's.
39
+ * @param path - An address from the site's own root, the mount already off,
40
+ * e.g. `/fr/tutorial`.
41
+ * @param languages - The languages this site is written in, the default one
42
+ * included.
43
+ * @returns The language it names and the page it opens.
44
+ */
45
+ export function readLanguagePath(
46
+ path: string,
47
+ languages: readonly Language[],
48
+ ): LanguagePath {
49
+ const opened = path.startsWith('/') ? path : `/${path}`;
50
+ const cut = opened.indexOf('/', 1);
51
+ const first = cut === -1 ? opened.slice(1) : opened.slice(1, cut);
52
+
53
+ for (const language of languages) {
54
+ if (language === DEFAULT_LANGUAGE || language !== first) continue;
55
+ const rest = cut === -1 ? '/' : opened.slice(cut);
56
+ return { language, path: trimTrailingSlash(rest) || '/' };
57
+ }
58
+ return {
59
+ language: DEFAULT_LANGUAGE,
60
+ path: trimTrailingSlash(opened) || '/',
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Write one of the site's own addresses in a language.
66
+ *
67
+ * The default language is written with no prefix: an address naming no language
68
+ * is English, which is what keeps every link handed out before the site was
69
+ * translated pointing at the same page.
70
+ * @param language - The language to write it in.
71
+ * @param path - The address from the site's own root, e.g. `/tutorial`.
72
+ * @returns The address under that language, e.g. `/fr/tutorial`.
73
+ */
74
+ export function withLanguagePath(language: Language, path: string): string {
75
+ const opened = path.startsWith('/') ? path : `/${path}`;
76
+ if (language === DEFAULT_LANGUAGE) return opened;
77
+ return opened === '/' ? `/${language}` : `/${language}${opened}`;
78
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The tags that tell a search engine five addresses are one page written five
3
+ * times.
4
+ *
5
+ * Without them a translated site is read as five sites that happen to look
6
+ * alike: the French tutorial competes with the English one instead of being
7
+ * offered to a French reader, and a search engine picks one of them to keep.
8
+ * With them each address is served to the audience it was written for, and the
9
+ * four it was not stop diluting it.
10
+ *
11
+ * Every page names **every** language including itself — a set that does not
12
+ * list the page it is on is ignored whole — plus `x-default`, which is where a
13
+ * reader whose language we do not speak is sent.
14
+ */
15
+
16
+ import type { Language } from '../../i18n/core/languages.ts';
17
+ import { DEFAULT_LANGUAGE } from '../../i18n/core/languages.ts';
18
+ import { withLanguagePath } from '../../language/core/languagePath.ts';
19
+ import { escapeAttribute } from '../../share/core/escape.ts';
20
+
21
+ /** Which page, in which languages, served from where. */
22
+ export interface AlternateOptions {
23
+ /**
24
+ * Where the site is served, mount path included and no trailing slash, e.g.
25
+ * `https://3d.cheminfo.org`.
26
+ */
27
+ origin: string;
28
+ /**
29
+ * The page, from the site's own root and with no language prefix on it, e.g.
30
+ * `/tutorial`.
31
+ */
32
+ path: string;
33
+ /**
34
+ * Every language the site is written in. One language alone means there is
35
+ * nothing to point at, and no tag is written.
36
+ */
37
+ languages: readonly Language[];
38
+ }
39
+
40
+ /**
41
+ * The `rel="alternate"` block for one page of a translated site.
42
+ * @param options - The page, its languages and where the site is served.
43
+ * @returns The tags, one per line, or `''` when the site speaks one language.
44
+ */
45
+ export function alternateLinkTags(options: AlternateOptions): string {
46
+ const { origin, path, languages } = options;
47
+ if (languages.length < 2) return '';
48
+
49
+ const tags: string[] = [];
50
+ for (const language of languages) {
51
+ tags.push(linkTag(language, address(origin, language, path)));
52
+ }
53
+ // A reader we have no language for gets the unprefixed address, which is the
54
+ // one every link handed out before the site was translated already points at.
55
+ tags.push(linkTag('x-default', address(origin, DEFAULT_LANGUAGE, path)));
56
+ return tags.join('\n');
57
+ }
58
+
59
+ function address(origin: string, language: Language, path: string): string {
60
+ return `${origin}${withLanguagePath(language, path)}`;
61
+ }
62
+
63
+ function linkTag(hreflang: string, href: string): string {
64
+ return `<link rel="alternate" hreflang="${escapeAttribute(hreflang)}" href="${escapeAttribute(href)}" />`;
65
+ }
@@ -10,10 +10,13 @@ export type {
10
10
  export { noscriptIndex } from './noscript.ts';
11
11
  export type { PageMetaOptions } from './pageMeta.ts';
12
12
  export { injectPageMeta, pageDocumentMeta, pageHeadTags } from './pageMeta.ts';
13
+ export type { PageContent, PageSection, PageTable } from './pageProse.ts';
14
+ export { pageProseHtml } from './pageProse.ts';
13
15
  export type { PlainDescriptionOptions } from './plainDescription.ts';
14
16
  export { plainDescription, plainProse } from './plainDescription.ts';
15
17
  export type { RobotsDisallow } from './robots.ts';
16
18
  export { robotsTxt } from './robots.ts';
19
+ export { routeProblems, withheldPhrase } from './routeProblems.ts';
17
20
  export type { RouteMeta } from './routes.ts';
18
21
  export { assertRoutes, homeRoute, pageMetaFor, routeFor } from './routes.ts';
19
22
  export type { SiteFilesOptions } from './siteFiles.ts';
@@ -12,6 +12,8 @@ import { ECOSYSTEM_SITES, siteUrl } from '../../ecosystem/core/sites.ts';
12
12
  import { joinBasePath } from '../../router/core/basePath.ts';
13
13
  import { escapeAttribute, escapeText } from '../../share/core/escape.ts';
14
14
 
15
+ import type { PageContent } from './pageProse.ts';
16
+ import { pageProseHtml } from './pageProse.ts';
15
17
  import type { RouteMeta } from './routes.ts';
16
18
  import type { SiteFilesOptions } from './siteFiles.ts';
17
19
  import { mountPathOf, resolveSite } from './siteFiles.ts';
@@ -93,6 +95,14 @@ export interface NoscriptOptions
93
95
  extends Omit<SiteFilesOptions, 'routes'>, Omit<NoscriptText, 'routes'> {
94
96
  /** The addresses it links, each with the label it is linked under. */
95
97
  routes: readonly NoscriptRoute[];
98
+ /**
99
+ * What the page being written says, above the crawl path. The block is the
100
+ * only text a crawler is handed before anything runs, so a page with nothing
101
+ * of its own here is indexed on its title and on this site's menu — which
102
+ * every other page of the site carries too.
103
+ * @default undefined — the block is the menu alone
104
+ */
105
+ content?: PageContent;
96
106
  }
97
107
 
98
108
  /**
@@ -111,14 +121,20 @@ export function noscriptIndex(options: NoscriptOptions): string {
111
121
  const site = resolveSite(options.site);
112
122
  const hrefs = options.hrefs ?? 'absolute';
113
123
  const mount = hrefs === 'absolute' ? mountPathOf(options) : '';
114
- const heading = escapeText(options.heading ?? siteDisplayName(site));
124
+ const content = options.content;
125
+ // The page's own name answers the title it is indexed under, so it wins over
126
+ // a heading the site writes for every one of its pages at once.
127
+ const heading = escapeText(
128
+ content?.heading ?? options.heading ?? siteDisplayName(site),
129
+ );
115
130
  const intro = escapeText(
116
131
  options.intro ??
117
132
  `${site.tagline} This tool needs JavaScript; these are the pages it offers:`,
118
133
  );
134
+ const prose = content === undefined ? '' : pageProseHtml(content, ' ');
119
135
 
120
136
  return `<noscript>
121
- <h1>${heading}</h1>
137
+ <h1>${heading}</h1>${prose}
122
138
  <p>${intro}</p>${pageList(options.routes, mount, hrefs, ' ')}${familyList(site.id, options.ecosystem)}
123
139
  </noscript>`;
124
140
  }