@mannisto/astro-metadata 1.0.2 → 2.0.0-alpha.2

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 (102) hide show
  1. package/README.md +427 -82
  2. package/package.json +25 -14
  3. package/src/assets/icons/LICENSE.md +35 -0
  4. package/src/assets/icons/binoculars.svg +1 -0
  5. package/src/assets/icons/caret-right.svg +1 -0
  6. package/src/assets/icons/check.svg +1 -0
  7. package/src/assets/icons/copy.svg +1 -0
  8. package/src/assets/icons/seal-check-duotone.svg +1 -0
  9. package/src/assets/icons/seal-question-duotone.svg +1 -0
  10. package/src/assets/icons/warning-circle-duotone.svg +1 -0
  11. package/src/assets/icons/warning-duotone.svg +1 -0
  12. package/src/assets/icons/x.svg +1 -0
  13. package/src/components/Metadata.astro +145 -0
  14. package/src/components/index.ts +2 -0
  15. package/src/integration.d.ts +112 -0
  16. package/src/integration.js +508 -0
  17. package/src/lib/checks.d.ts +70 -0
  18. package/src/lib/checks.js +193 -0
  19. package/src/lib/config.ts +111 -0
  20. package/src/lib/favicon-files.d.ts +29 -0
  21. package/src/lib/favicon-files.js +103 -0
  22. package/src/lib/favicon-image.ts +124 -0
  23. package/src/lib/favicon.ts +167 -0
  24. package/src/lib/head.d.ts +25 -0
  25. package/src/lib/head.js +141 -0
  26. package/src/lib/manifest.ts +126 -0
  27. package/src/lib/rules.d.ts +73 -0
  28. package/src/lib/rules.js +929 -0
  29. package/src/lib/schema.ts +208 -0
  30. package/src/lib/site.ts +72 -0
  31. package/src/lib/url.ts +79 -0
  32. package/src/routes/favicon.ts +14 -0
  33. package/src/routes/manifest.ts +18 -0
  34. package/src/runtime.ts +16 -0
  35. package/src/toolbar/app.ts +72 -0
  36. package/src/toolbar/assets.ts +81 -0
  37. package/src/toolbar/data.ts +282 -0
  38. package/src/toolbar/define.ts +7 -0
  39. package/src/toolbar/env.d.ts +10 -0
  40. package/src/toolbar/highlight.ts +83 -0
  41. package/src/toolbar/styles/shared.css +18 -0
  42. package/src/toolbar/styles/tokens.css +182 -0
  43. package/src/toolbar/toolbar-badge.ts +78 -0
  44. package/src/toolbar/ui/alert/alert.css +126 -0
  45. package/src/toolbar/ui/alert/alert.ts +102 -0
  46. package/src/toolbar/ui/badge/badge.css +41 -0
  47. package/src/toolbar/ui/badge/badge.ts +24 -0
  48. package/src/toolbar/ui/code-block/code-block.css +90 -0
  49. package/src/toolbar/ui/code-block/code-block.ts +68 -0
  50. package/src/toolbar/ui/color-swatch/color-swatch.css +15 -0
  51. package/src/toolbar/ui/color-swatch/color-swatch.ts +31 -0
  52. package/src/toolbar/ui/icon/icon.css +11 -0
  53. package/src/toolbar/ui/icon/icon.ts +46 -0
  54. package/src/toolbar/ui/image/image.css +18 -0
  55. package/src/toolbar/ui/image/image.ts +58 -0
  56. package/src/toolbar/ui/inline-code/inline-code.css +16 -0
  57. package/src/toolbar/ui/inline-code/inline-code.ts +41 -0
  58. package/src/toolbar/ui/section/section.css +88 -0
  59. package/src/toolbar/ui/section/section.ts +78 -0
  60. package/src/toolbar/ui/tab/tab.css +7 -0
  61. package/src/toolbar/ui/tab/tab.ts +47 -0
  62. package/src/toolbar/ui/tab-group/tab-group.css +42 -0
  63. package/src/toolbar/ui/tab-group/tab-group.ts +101 -0
  64. package/src/toolbar/ui/table/table.css +124 -0
  65. package/src/toolbar/ui/table/table.ts +155 -0
  66. package/src/toolbar/views/code-blocks.ts +35 -0
  67. package/src/toolbar/views/metadata-panel.css +105 -0
  68. package/src/toolbar/views/metadata-panel.ts +125 -0
  69. package/src/toolbar/views/overview-view.css +27 -0
  70. package/src/toolbar/views/overview-view.ts +401 -0
  71. package/src/toolbar/views/previews/discord.css +70 -0
  72. package/src/toolbar/views/previews/discord.ts +60 -0
  73. package/src/toolbar/views/previews/facebook.css +65 -0
  74. package/src/toolbar/views/previews/facebook.ts +36 -0
  75. package/src/toolbar/views/previews/google.css +75 -0
  76. package/src/toolbar/views/previews/google.ts +36 -0
  77. package/src/toolbar/views/previews/linkedin.css +58 -0
  78. package/src/toolbar/views/previews/linkedin.ts +34 -0
  79. package/src/toolbar/views/previews/page.ts +129 -0
  80. package/src/toolbar/views/previews/platform.ts +14 -0
  81. package/src/toolbar/views/previews/previews-view.css +37 -0
  82. package/src/toolbar/views/previews/previews-view.ts +52 -0
  83. package/src/toolbar/views/previews/slack.css +61 -0
  84. package/src/toolbar/views/previews/slack.ts +47 -0
  85. package/src/toolbar/views/previews/twitter.css +89 -0
  86. package/src/toolbar/views/previews/twitter.ts +57 -0
  87. package/src/toolbar/views/previews/whatsapp.css +88 -0
  88. package/src/toolbar/views/previews/whatsapp.ts +65 -0
  89. package/src/toolbar/views/raw-view.ts +41 -0
  90. package/index.ts +0 -31
  91. package/src/components/Canonical.astro +0 -11
  92. package/src/components/Description.astro +0 -9
  93. package/src/components/Favicon.astro +0 -96
  94. package/src/components/Head.astro +0 -94
  95. package/src/components/Keywords.astro +0 -9
  96. package/src/components/LanguageAlternates.astro +0 -18
  97. package/src/components/OpenGraph.astro +0 -75
  98. package/src/components/Robots.astro +0 -25
  99. package/src/components/Schema.astro +0 -18
  100. package/src/components/Title.astro +0 -13
  101. package/src/components/Twitter.astro +0 -27
  102. package/src/lib/metadata.ts +0 -77
@@ -0,0 +1,167 @@
1
+ import { getConfig } from "./config.ts"
2
+ import type { ResolvedFaviconFile } from "./favicon-files.js"
3
+ import { withBase } from "./url.ts"
4
+
5
+ /**
6
+ * The favicon data that the integration gives to the components and the endpoints.
7
+ */
8
+ export type FaviconInfo = {
9
+ /** The absolute path of the source file. */
10
+ source: string
11
+ /** The background color of the Apple icon. */
12
+ background: string
13
+ /** True for an SVG source. Only an SVG source can give an SVG file. */
14
+ svg: boolean
15
+ /** A short hash of the source file, for cache busting. */
16
+ hash: string
17
+ /** The absolute path of the sharp package. */
18
+ sharp: string
19
+ /** The files to generate, with their formats and uses. */
20
+ files: ResolvedFaviconFile[]
21
+ }
22
+
23
+ /**
24
+ * The favicon config: the `favicon` option after the checks.
25
+ */
26
+ export type FaviconConfig = Omit<FaviconInfo, "sharp">
27
+
28
+ /**
29
+ * An icon entry for the `icons` field of a web app manifest.
30
+ */
31
+ export type ManifestIcon = {
32
+ src: string
33
+ sizes: string
34
+ type: string
35
+ }
36
+
37
+ /**
38
+ * A link tag of a generated favicon file.
39
+ */
40
+ export type FaviconLink = {
41
+ rel: "icon" | "apple-touch-icon"
42
+ href: string
43
+ type?: string
44
+ sizes?: string
45
+ }
46
+
47
+ // The order of the link tags: ICO first, then the PNG files, the SVG and the Apple icon
48
+ const LINK_ORDER = { ico: 0, png: 1, svg: 2 }
49
+
50
+ const MIME_TYPES = {
51
+ ico: "image/x-icon",
52
+ png: "image/png",
53
+ svg: "image/svg+xml",
54
+ }
55
+
56
+ /**
57
+ * Returns the size of a file for its link tag: the largest image of an ICO file,
58
+ * or undefined for an SVG file.
59
+ */
60
+ function largestSize(file: ResolvedFaviconFile): number | undefined {
61
+ return file.sizes.length > 0 ? Math.max(...file.sizes) : undefined
62
+ }
63
+
64
+ /**
65
+ * The favicons that the `favicon` option of the integration generates.
66
+ * Without the option, the lists are empty and `config` is undefined.
67
+ */
68
+ export const Favicon = {
69
+ /**
70
+ * Returns the favicon config: the source, the background color and the files.
71
+ */
72
+ get config(): FaviconConfig | undefined {
73
+ const favicon = getConfig().favicon
74
+ if (!favicon) return undefined
75
+
76
+ return {
77
+ source: favicon.source,
78
+ background: favicon.background,
79
+ svg: favicon.svg,
80
+ hash: favicon.hash,
81
+ files: favicon.files,
82
+ }
83
+ },
84
+
85
+ /**
86
+ * Returns the generated files, with their formats, sizes and uses.
87
+ */
88
+ get files(): ResolvedFaviconFile[] {
89
+ return getConfig().favicon?.files ?? []
90
+ },
91
+
92
+ /**
93
+ * Returns the URL of a generated file, with the base and a hash of the source.
94
+ * The hash changes when the source changes, so browsers load the new icon.
95
+ *
96
+ * @example Favicon.url("/favicon.ico") // "/favicon.ico?v=3f2a1c9e"
97
+ */
98
+ url(path: string): string {
99
+ const hash = getConfig().favicon?.hash
100
+ return hash ? `${withBase(path)}?v=${hash}` : withBase(path)
101
+ },
102
+
103
+ /**
104
+ * Returns the link tags of the generated files, in the recommended order:
105
+ * the ICO file, the PNG files, the SVG file and the Apple icon.
106
+ */
107
+ links(): FaviconLink[] {
108
+ const order = (file: ResolvedFaviconFile) => {
109
+ return file.use === "apple" ? 3 : LINK_ORDER[file.type]
110
+ }
111
+ const files = Favicon.files
112
+ .filter((file) => {
113
+ return file.use !== "manifest"
114
+ })
115
+ .sort((a, b) => {
116
+ return order(a) - order(b) || (a.sizes[0] ?? 0) - (b.sizes[0] ?? 0)
117
+ })
118
+
119
+ return files.map((file) => {
120
+ const size = largestSize(file)
121
+ if (file.use === "apple") {
122
+ return {
123
+ rel: "apple-touch-icon",
124
+ href: Favicon.url(file.path),
125
+ sizes: `${size}x${size}`,
126
+ }
127
+ }
128
+ return {
129
+ rel: "icon",
130
+ href: Favicon.url(file.path),
131
+ type: MIME_TYPES[file.type],
132
+ sizes: size ? `${size}x${size}` : undefined,
133
+ }
134
+ })
135
+ },
136
+
137
+ /**
138
+ * Returns the icons for the `icons` field of a web app manifest: the PNG files
139
+ * of 192 px or larger.
140
+ *
141
+ * @example
142
+ * // src/pages/manifest.webmanifest.ts
143
+ * export const GET = () => Response.json({ name: "My Site", icons: Favicon.icons() })
144
+ */
145
+ icons(): ManifestIcon[] {
146
+ return Favicon.files
147
+ .filter((file) => {
148
+ return file.use === "manifest"
149
+ })
150
+ .map((file) => {
151
+ const size = file.sizes[0]
152
+ return { src: Favicon.url(file.path), sizes: `${size}x${size}`, type: "image/png" }
153
+ })
154
+ },
155
+
156
+ /**
157
+ * Generates a favicon file of the config. Returns a 404 response for a path that
158
+ * is not in the config.
159
+ *
160
+ * @param path - The path of the file, e.g. "/favicon-32.png".
161
+ */
162
+ async generate(path: string): Promise<Response> {
163
+ // Loaded only here: the image code needs Node and sharp
164
+ const { renderFavicon } = await import("./favicon-image.ts")
165
+ return renderFavicon(path)
166
+ },
167
+ }
@@ -0,0 +1,25 @@
1
+ /** The metadata of a page, read from its HTML. */
2
+ export type HeadData = {
3
+ /** The `lang` attribute of the html tag. */
4
+ lang?: string
5
+ /** The value of `<meta charset>`. */
6
+ charset?: string
7
+ /** The texts of all title tags. */
8
+ titles: string[]
9
+ /** The first `content` of each meta `name`, `property` or `http-equiv`, in lowercase. */
10
+ meta: Record<string, string>
11
+ /** The number of meta tags with each `name`, `property` or `http-equiv`. */
12
+ metaCount: Record<string, number>
13
+ /** The attributes of each link tag. */
14
+ links: Record<string, string>[]
15
+ /** The texts of the JSON-LD scripts. */
16
+ schemas: string[]
17
+ }
18
+
19
+ export declare function parseHead(html: string): HeadData
20
+
21
+ export declare function findLinks(head: HeadData, rel: string): Record<string, string>[]
22
+
23
+ export declare function findLink(head: HeadData, rel: string): string | undefined
24
+
25
+ export declare function isAbsolute(value: string): boolean
@@ -0,0 +1,141 @@
1
+ // Plain JavaScript: the integration runs in Node, which does not strip types in node_modules.
2
+ // The dev toolbar app uses the same functions, so the build and the toolbar give the same results.
3
+
4
+ const ENTITIES = {
5
+ amp: "&",
6
+ lt: "<",
7
+ gt: ">",
8
+ quot: '"',
9
+ apos: "'",
10
+ nbsp: " ",
11
+ }
12
+
13
+ // A tag with its attributes. Quoted values can contain ">".
14
+ const TAG = /<(meta|link)\b((?:[^>"']|"[^"]*"|'[^']*')*)>/gi
15
+ const ATTRIBUTE = /([^\s=/>"']+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g
16
+ const SCRIPT = /<script\b((?:[^>"']|"[^"]*"|'[^']*')*)>([\s\S]*?)<\/script>/gi
17
+
18
+ /**
19
+ * Replaces the HTML entities in a text with their characters.
20
+ *
21
+ * @param {string} text
22
+ * @returns {string}
23
+ */
24
+ function decode(text) {
25
+ return text.replace(/&(#x[\da-f]+|#\d+|[a-z]+);/gi, (match, code) => {
26
+ if (code[0] !== "#") return ENTITIES[code.toLowerCase()] ?? match
27
+
28
+ const hex = code[1].toLowerCase() === "x"
29
+ const point = hex ? parseInt(code.slice(2), 16) : parseInt(code.slice(1), 10)
30
+ return point <= 0x10ffff ? String.fromCodePoint(point) : match
31
+ })
32
+ }
33
+
34
+ /**
35
+ * Returns the attributes of a tag. The names are in lowercase, the values are decoded.
36
+ *
37
+ * @param {string} source - The text between the tag name and ">".
38
+ * @returns {Record<string, string>}
39
+ */
40
+ function parseAttributes(source) {
41
+ /** @type {Record<string, string>} */
42
+ const attributes = {}
43
+ for (const match of source.matchAll(ATTRIBUTE)) {
44
+ const value = match[2] ?? match[3] ?? match[4] ?? ""
45
+ attributes[match[1].toLowerCase()] = decode(value)
46
+ }
47
+ return attributes
48
+ }
49
+
50
+ /**
51
+ * Reads the metadata of a page from its HTML.
52
+ *
53
+ * @param {string} html - The HTML of the page.
54
+ * @returns {import("./head").HeadData}
55
+ */
56
+ export function parseHead(html) {
57
+ const end = html.search(/<\/head>/i)
58
+ const head = end === -1 ? html : html.slice(0, end)
59
+
60
+ /** @type {Record<string, string>} */
61
+ const meta = {}
62
+ /** @type {Record<string, number>} */
63
+ const metaCount = {}
64
+ /** @type {Record<string, string>[]} */
65
+ const links = []
66
+ let charset
67
+
68
+ for (const match of head.matchAll(TAG)) {
69
+ const attributes = parseAttributes(match[2])
70
+ if (match[1].toLowerCase() === "link") {
71
+ links.push(attributes)
72
+ continue
73
+ }
74
+ if (attributes.charset !== undefined) charset = attributes.charset
75
+
76
+ const key = (attributes.name ?? attributes.property ?? attributes["http-equiv"])?.toLowerCase()
77
+ if (!key || attributes.content === undefined) continue
78
+
79
+ metaCount[key] = (metaCount[key] ?? 0) + 1
80
+ // Keep the first value, as crawlers do
81
+ if (!(key in meta)) meta[key] = attributes.content
82
+ }
83
+
84
+ const titles = [...head.matchAll(/<title\b[^>]*>([\s\S]*?)<\/title>/gi)].map((match) => {
85
+ return decode(match[1]).trim()
86
+ })
87
+
88
+ // JSON-LD can also be in the body
89
+ const schemas = [...html.matchAll(SCRIPT)]
90
+ .filter((match) => {
91
+ return /application\/ld\+json/i.test(parseAttributes(match[1]).type ?? "")
92
+ })
93
+ .map((match) => {
94
+ return match[2]
95
+ })
96
+
97
+ const lang = /<html\b(?:[^>"']|"[^"]*"|'[^']*')*>/i.exec(html)
98
+ return {
99
+ lang: lang ? parseAttributes(lang[0].slice(5, -1)).lang : undefined,
100
+ charset,
101
+ titles,
102
+ meta,
103
+ metaCount,
104
+ links,
105
+ schemas,
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Returns the link tags with a `rel` value, e.g. all "alternate" links.
111
+ *
112
+ * @param {import("./head").HeadData} head
113
+ * @param {string} rel - e.g. "canonical".
114
+ * @returns {Record<string, string>[]}
115
+ */
116
+ export function findLinks(head, rel) {
117
+ return head.links.filter((attributes) => {
118
+ return (attributes.rel ?? "").toLowerCase().split(/\s+/).includes(rel)
119
+ })
120
+ }
121
+
122
+ /**
123
+ * Returns the `href` of the first link with a `rel` value.
124
+ *
125
+ * @param {import("./head").HeadData} head
126
+ * @param {string} rel - e.g. "canonical".
127
+ * @returns {string | undefined}
128
+ */
129
+ export function findLink(head, rel) {
130
+ return findLinks(head, rel)[0]?.href
131
+ }
132
+
133
+ /**
134
+ * Returns true for a URL with "http:" or "https:".
135
+ *
136
+ * @param {string} value
137
+ * @returns {boolean}
138
+ */
139
+ export function isAbsolute(value) {
140
+ return /^https?:\/\//i.test(value)
141
+ }
@@ -0,0 +1,126 @@
1
+ import { getConfig, localize } from "./config.ts"
2
+ import type { Localized } from "./config.ts"
3
+ import { Favicon } from "./favicon.ts"
4
+ import { withBase } from "./url.ts"
5
+
6
+ /**
7
+ * The web app manifest to generate. Text values can have one value for each locale:
8
+ * then each locale gets its own manifest file.
9
+ */
10
+ export type ManifestConfig = {
11
+ name: Localized
12
+ shortName?: Localized
13
+ description?: Localized
14
+ /** How the installed app opens. Defaults to "standalone". */
15
+ display?: "standalone" | "fullscreen" | "minimal-ui" | "browser"
16
+ /** The page that the installed app opens. Defaults to the site root. */
17
+ startUrl?: Localized
18
+ /** Defaults to the `themeColor` of the config, or its light color. */
19
+ themeColor?: string
20
+ backgroundColor?: string
21
+ /** Other manifest fields, e.g. `shortcuts`, copied into the file unchanged. */
22
+ extra?: Record<string, unknown>
23
+ }
24
+
25
+ /**
26
+ * A generated manifest file: its locale, and its folder when it is not at the root.
27
+ */
28
+ export type ManifestPath = {
29
+ /** The folder of the file, e.g. "fi" for /fi/manifest.webmanifest. */
30
+ lang?: string
31
+ /** The locale of the texts in the file. */
32
+ locale?: string
33
+ }
34
+
35
+ /**
36
+ * Returns the manifest to generate, or undefined for no manifest or your own manifest.
37
+ */
38
+ function generated(): ManifestConfig | undefined {
39
+ const manifest = getConfig().manifest
40
+ return typeof manifest === "object" ? manifest : undefined
41
+ }
42
+
43
+ /**
44
+ * The web app manifest of the `manifest` option: an object generates the manifest,
45
+ * and a string is the path of your own manifest.
46
+ */
47
+ export const Manifest = {
48
+ /**
49
+ * Returns the `manifest` option, or undefined without it.
50
+ */
51
+ get config(): ManifestConfig | string | undefined {
52
+ return getConfig().manifest
53
+ },
54
+
55
+ /**
56
+ * Returns the generated manifest files. The first locale of the texts uses
57
+ * /manifest.webmanifest, the other locales use /<locale>/manifest.webmanifest.
58
+ * Returns an empty list for no manifest or your own manifest.
59
+ */
60
+ paths(): ManifestPath[] {
61
+ const manifest = generated()
62
+ if (!manifest) return []
63
+
64
+ // The locales are the keys of the first localized text
65
+ const texts = [manifest.name, manifest.shortName, manifest.description, manifest.startUrl]
66
+ const localized = texts.find((text) => {
67
+ return typeof text === "object"
68
+ })
69
+ if (!localized) return [{}]
70
+
71
+ return Object.keys(localized).map((locale, index) => {
72
+ return { lang: index === 0 ? undefined : locale, locale }
73
+ })
74
+ },
75
+
76
+ /**
77
+ * Returns the URL of the manifest for a locale, with the base.
78
+ * An unknown locale gets the manifest at the root.
79
+ *
80
+ * @example Manifest.url("fi") // "/fi/manifest.webmanifest"
81
+ */
82
+ url(locale?: string): string | undefined {
83
+ const manifest = getConfig().manifest
84
+ if (!manifest) return undefined
85
+ if (typeof manifest === "string") return withBase(manifest)
86
+
87
+ const path = Manifest.paths().find((entry) => {
88
+ return entry.locale === locale
89
+ })
90
+ return withBase(path?.lang ? `/${path.lang}/manifest.webmanifest` : "/manifest.webmanifest")
91
+ },
92
+
93
+ /**
94
+ * Builds the manifest for a locale, with the generated favicons as its icons.
95
+ * Returns undefined for no manifest or your own manifest.
96
+ */
97
+ build(locale?: string): Record<string, unknown> | undefined {
98
+ const manifest = generated()
99
+ if (!manifest) return undefined
100
+
101
+ const siteColor = getConfig().themeColor
102
+ const themeColor =
103
+ manifest.themeColor ?? (typeof siteColor === "string" ? siteColor : siteColor?.light)
104
+ const icons = Favicon.icons()
105
+
106
+ const json: Record<string, unknown> = {
107
+ name: localize(manifest.name, locale),
108
+ short_name: localize(manifest.shortName, locale),
109
+ description: localize(manifest.description, locale),
110
+ lang: locale,
111
+ start_url: withBase(localize(manifest.startUrl, locale) ?? "/"),
112
+ scope: withBase("/"),
113
+ display: manifest.display ?? "standalone",
114
+ theme_color: themeColor,
115
+ background_color: manifest.backgroundColor,
116
+ icons: icons.length > 0 ? icons : undefined,
117
+ ...manifest.extra,
118
+ }
119
+
120
+ // Remove empty fields, so the file has only the values that the config gives
121
+ for (const key of Object.keys(json)) {
122
+ if (json[key] === undefined) delete json[key]
123
+ }
124
+ return json
125
+ },
126
+ }
@@ -0,0 +1,73 @@
1
+ import type { HeadData } from "./head"
2
+
3
+ type RuleBase = {
4
+ /** A short, unique name, e.g. "og-title-missing". The `rules` option turns a rule off by its id. */
5
+ id: string
6
+ /**
7
+ * The tag of the problem, e.g. "og:title", "title", "hreflang" or "json-ld".
8
+ * The dev toolbar app opens the row of this tag.
9
+ */
10
+ field: string
11
+ level: "error" | "warning" | "info"
12
+ }
13
+
14
+ /** A rule that checks the head of one page. */
15
+ export type Rule = RuleBase & {
16
+ /** True for a search or sharing rule: it does not run on a noindex page. */
17
+ indexed?: boolean
18
+ /** Returns the message of a problem, or undefined. Text between backticks shows as code. */
19
+ check(head: HeadData): string | undefined
20
+ }
21
+
22
+ /** A built page: its path in the output folder, and its head. */
23
+ export type SitePage = {
24
+ path: string
25
+ head: HeadData
26
+ }
27
+
28
+ /** A problem of one page, found by a site rule. */
29
+ export type SiteProblem = {
30
+ path: string
31
+ message: string
32
+ }
33
+
34
+ /** A rule that compares the pages of the site. Only the build runs it. */
35
+ export type SiteRule = RuleBase & {
36
+ check(pages: SitePage[]): SiteProblem[]
37
+ }
38
+
39
+ /** A loaded file of the page. */
40
+ export type Asset = {
41
+ /** The URL or path of the file. */
42
+ url: string
43
+ /** False when the file does not load. */
44
+ ok: boolean
45
+ /** The HTTP status of a file that does not load. */
46
+ status?: number
47
+ /** The MIME type, e.g. "image/png". */
48
+ type?: string
49
+ /** The file size in bytes. */
50
+ bytes?: number
51
+ /** The real size of an image, in px. */
52
+ width?: number
53
+ height?: number
54
+ }
55
+
56
+ /** The loaded files of a page. A file that can not be checked, e.g. on another site, is not in the list. */
57
+ export type PageAssets = {
58
+ image?: Asset
59
+ icons: Asset[]
60
+ }
61
+
62
+ /** A rule that checks the files of the page. */
63
+ export type AssetRule = RuleBase & {
64
+ check(head: HeadData, assets: PageAssets): string | undefined
65
+ }
66
+
67
+ export declare const LIMITS: { title: number; description: number; shortDescription: number }
68
+
69
+ export declare const RULES: Rule[]
70
+
71
+ export declare const SITE_RULES: SiteRule[]
72
+
73
+ export declare const ASSET_RULES: AssetRule[]