@mannisto/astro-metadata 1.0.0 → 1.0.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.
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@mannisto/astro-metadata",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Astro components for managing your page head — metadata, social sharing, favicons, and SEO.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/eremannisto/astro-metadata#readme",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/eremannisto/astro-metadata"
10
+ "url": "git+https://github.com/eremannisto/astro-metadata.git"
11
11
  },
12
12
  "bugs": {
13
13
  "url": "https://github.com/eremannisto/astro-metadata/issues"
@@ -23,25 +23,31 @@
23
23
  "src",
24
24
  "index.ts"
25
25
  ],
26
+ "scripts": {
27
+ "lint": "eslint . --fix",
28
+ "format": "prettier --write .",
29
+ "check": "eslint . && prettier --check .",
30
+ "test": "pnpm run test:unit && pnpm run test:e2e",
31
+ "test:unit": "vitest run",
32
+ "test:e2e": "pnpm test:fixtures && playwright test",
33
+ "test:fixtures": "pnpm --filter \"./tests/e2e/fixtures/**\" exec astro build"
34
+ },
26
35
  "peerDependencies": {
27
- "astro": "^4.3.0 || ^5.0.0"
36
+ "astro": "^4.3.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
28
37
  },
29
38
  "devDependencies": {
30
- "@biomejs/biome": "^2.4.3",
39
+ "@eslint/js": "^10.0.1",
40
+ "@ianvs/prettier-plugin-sort-imports": "^4.7.1",
31
41
  "@playwright/test": "^1.58.2",
32
- "@types/node": "^25.3.0",
33
- "astro": "^5.17.2",
42
+ "@types/node": "^22.19.15",
43
+ "astro": "^6.4.8",
44
+ "eslint": "^10.0.3",
45
+ "eslint-config-prettier": "^10.1.8",
34
46
  "prettier": "^3.8.1",
35
47
  "prettier-plugin-astro": "^0.14.1",
36
48
  "typescript": "^5.9.3",
37
- "vitest": "^4.0.18"
49
+ "typescript-eslint": "^8.57.0",
50
+ "vitest": "^4.1.0"
38
51
  },
39
- "scripts": {
40
- "lint": "biome lint ./src ./index.ts",
41
- "format": "prettier --write .",
42
- "check": "biome lint ./src ./index.ts && prettier --check .",
43
- "test": "pnpm run test:unit && pnpm run test:e2e",
44
- "test:unit": "vitest run",
45
- "test:e2e": "playwright test"
46
- }
47
- }
52
+ "packageManager": "pnpm@10.32.1"
53
+ }
@@ -14,6 +14,19 @@ export type Props = {
14
14
 
15
15
  const { icons, manifest, sort = true } = Astro.props
16
16
 
17
+ /**
18
+ * Returns the file extension of a favicon path in lowercase, without the
19
+ * query string or the hash. For example, "/icon.SVG?v=2" gives "svg".
20
+ *
21
+ * @param path - The path to the favicon file.
22
+ * @returns The file extension, or an empty string if the path has none.
23
+ */
24
+ function getExtension(path: string): string {
25
+ const file = path.split(/[?#]/)[0].toLowerCase()
26
+ const dot = file.lastIndexOf(".")
27
+ return dot === -1 ? "" : file.slice(dot + 1)
28
+ }
29
+
17
30
  /**
18
31
  * Detects the MIME type of a favicon file from its extension.
19
32
  *
@@ -21,11 +34,11 @@ const { icons, manifest, sort = true } = Astro.props
21
34
  * @returns The MIME type of the file.
22
35
  */
23
36
  function getMimeType(path: string): string {
24
- if (path.endsWith(".svg")) return "image/svg+xml"
25
- if (path.endsWith(".png")) return "image/png"
26
- if (path.endsWith(".webp")) return "image/webp"
27
- if (path.endsWith(".jpg") || path.endsWith(".jpeg")) return "image/jpeg"
28
- if (path.endsWith(".ico")) return "image/x-icon"
37
+ const extension = getExtension(path)
38
+ if (extension === "svg") return "image/svg+xml"
39
+ if (extension === "png") return "image/png"
40
+ if (extension === "webp") return "image/webp"
41
+ if (extension === "jpg" || extension === "jpeg") return "image/jpeg"
29
42
  return "image/x-icon"
30
43
  }
31
44
 
@@ -50,15 +63,10 @@ function getMedia(theme?: "light" | "dark"): string | undefined {
50
63
  function getSortOrder(file: FaviconFile): number {
51
64
  if (file.apple) return 4
52
65
  if (file.theme) return 5
53
- if (file.path.endsWith(".ico")) return 0
54
- if (
55
- file.path.endsWith(".png") ||
56
- file.path.endsWith(".webp") ||
57
- file.path.endsWith(".jpg") ||
58
- file.path.endsWith(".jpeg")
59
- )
60
- return 1
61
- if (file.path.endsWith(".svg")) return 2
66
+ const extension = getExtension(file.path)
67
+ if (extension === "ico") return 0
68
+ if (["png", "webp", "jpg", "jpeg"].includes(extension)) return 1
69
+ if (extension === "svg") return 2
62
70
  return 3
63
71
  }
64
72
 
@@ -1,16 +1,17 @@
1
1
  ---
2
2
  import type { ComponentProps } from "astro/types"
3
- import Title from "./Title.astro"
4
- import Description from "./Description.astro"
3
+
5
4
  import Canonical from "./Canonical.astro"
6
- import Keywords from "./Keywords.astro"
7
- import Robots from "./Robots.astro"
8
- import OpenGraph from "./OpenGraph.astro"
9
- import Twitter from "./Twitter.astro"
5
+ import Description from "./Description.astro"
10
6
  import Favicon from "./Favicon.astro"
11
- import Schema from "./Schema.astro"
7
+ import Keywords from "./Keywords.astro"
12
8
  import LanguageAlternates from "./LanguageAlternates.astro"
9
+ import OpenGraph from "./OpenGraph.astro"
13
10
  import type { OpenGraphImage } from "./OpenGraph.astro"
11
+ import Robots from "./Robots.astro"
12
+ import Schema from "./Schema.astro"
13
+ import Title from "./Title.astro"
14
+ import Twitter from "./Twitter.astro"
14
15
 
15
16
  export type Props = {
16
17
  title: string
@@ -67,16 +68,14 @@ const tw =
67
68
  title,
68
69
  description: enabled(description) ? description : undefined,
69
70
  image: enabled(image) ? image : undefined,
70
- url: enabled(canonical) ? canonical : undefined,
71
71
  ...twitter,
72
72
  }
73
73
  : false
74
74
  ---
75
75
 
76
76
  <head>
77
- <slot name="top" />
78
-
79
77
  <meta charset={charset} />
78
+ <slot name="top" />
80
79
  <meta name="viewport" content={viewport} />
81
80
 
82
81
  <Title value={title} template={titleTemplate} />
@@ -4,6 +4,15 @@ export type Props = {
4
4
  }
5
5
 
6
6
  const { schema } = Astro.props
7
+
8
+ // Escape the characters that can end the script tag or break the JavaScript parser.
9
+ // A value such as "</script>" in the schema then stays inside the JSON.
10
+ const json = JSON.stringify(schema)
11
+ .replace(/</g, "\\u003c")
12
+ .replace(/>/g, "\\u003e")
13
+ .replace(/&/g, "\\u0026")
14
+ .replace(/\u2028/g, "\\u2028")
15
+ .replace(/\u2029/g, "\\u2029")
7
16
  ---
8
17
 
9
- <script is:inline type="application/ld+json" set:html={JSON.stringify(schema)} />
18
+ <script is:inline type="application/ld+json" set:html={json} />
@@ -5,6 +5,9 @@ export type Props = {
5
5
  }
6
6
 
7
7
  const { value, template = "%s" } = Astro.props
8
+
9
+ // A replacer function keeps "$&" and other "$" patterns in the title as text
10
+ const title = template.replace("%s", () => value)
8
11
  ---
9
12
 
10
- <title>{template.replace("%s", value)}</title>
13
+ <title>{title}</title>
@@ -2,6 +2,9 @@
2
2
  export type Props = {
3
3
  title?: string
4
4
  description?: string
5
+ /**
6
+ * @deprecated X does not read `twitter:url`. The component ignores this value.
7
+ */
5
8
  url?: string
6
9
  card?: "summary" | "summary_large_image" | "player" | "app"
7
10
  site?: string
@@ -12,13 +15,12 @@ export type Props = {
12
15
  }
13
16
  }
14
17
 
15
- const { title, description, url, card = "summary_large_image", site, creator, image } = Astro.props
18
+ const { title, description, card = "summary_large_image", site, creator, image } = Astro.props
16
19
  ---
17
20
 
18
21
  {card && <meta name="twitter:card" content={card} />}
19
22
  {title && <meta name="twitter:title" content={title} />}
20
23
  {description && <meta name="twitter:description" content={description} />}
21
- {url && <meta name="twitter:url" content={url} />}
22
24
  {image?.url && <meta name="twitter:image" content={image.url} />}
23
25
  {image?.alt && <meta name="twitter:image:alt" content={image.alt} />}
24
26
  {site && <meta name="twitter:site" content={site} />}
@@ -1,4 +1,5 @@
1
1
  import type { ComponentProps } from "astro/types"
2
+
2
3
  import type Head from "../components/Head.astro"
3
4
 
4
5
  let store: Partial<ComponentProps<typeof Head>> = {}