astro-accelerator 5.9.20 → 5.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/astro.config.mjs CHANGED
@@ -3,6 +3,7 @@
3
3
  import mdx from '@astrojs/mdx';
4
4
  import remarkDirective from 'remark-directive';
5
5
  import { defineConfig } from 'astro/config';
6
+ import { readingTime } from '/src/themes/accelerator/utilities/reading-time.mjs';
6
7
  import { defaultLayout } from '/src/themes/accelerator/utilities/default-layout.mjs';
7
8
  import {
8
9
  attributeMarkdown,
@@ -19,6 +20,7 @@ export default defineConfig({
19
20
  remarkDirective,
20
21
  attributeMarkdown,
21
22
  wrapTables,
23
+ readingTime,
22
24
  ],
23
25
  closeSelfClosing: false,
24
26
  extendDefaultPlugins: true,
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.9.20",
2
+ "version": "5.10.0",
3
3
  "author": "Steve Fenton",
4
4
  "name": "astro-accelerator",
5
5
  "description": "A super-lightweight, accessible, SEO-friendly starter project for Astro",
@@ -35,8 +35,8 @@
35
35
  "dependencies": {
36
36
  "@astrojs/mdx": "^4.3.4",
37
37
  "@img/sharp-linux-x64": "^0.34.3",
38
- "astro": "^5.13.4",
39
- "astro-accelerator-utils": "^0.3.52",
38
+ "astro": "^5.13.5",
39
+ "astro-accelerator-utils": "^0.3.53",
40
40
  "cspell": "^8.19.4",
41
41
  "csv": "^6.4.1",
42
42
  "glob": "^11.0.3",
@@ -821,9 +821,22 @@ nav.site-nav h2 {
821
821
 
822
822
  .post-meta > .author-info {
823
823
  display: flex;
824
+ flex-wrap: wrap;
824
825
  align-items: center;
825
826
  }
826
827
 
828
+ .post-meta.mini > .author-info > *:nth-child(n + 3)::before {
829
+ content: '|';
830
+ padding: 0 0.3rem;
831
+ opacity: 0.5;
832
+ }
833
+
834
+ .post-meta.mini > .author-info > .author-image {
835
+ height: 1rem;
836
+ width: 1rem;
837
+ margin: 0 0.3rem 0 0;
838
+ }
839
+
827
840
  .post-meta time[itemprop='datePublished'] {
828
841
  display: inline-block;
829
842
  }
package/src/config.ts CHANGED
@@ -33,6 +33,11 @@ const SITE: Site = {
33
33
  month: 'long',
34
34
  day: 'numeric',
35
35
  },
36
+ shortDateOptions: {
37
+ year: 'numeric',
38
+ month: 'short',
39
+ day: 'numeric',
40
+ },
36
41
  cacheMaxAge: 200,
37
42
  featureFlags: {
38
43
  stickyNav: { top: 100 },
@@ -4,16 +4,20 @@
4
4
  import { Accelerator } from 'astro-accelerator-utils';
5
5
  import type { Frontmatter } from 'astro-accelerator-utils/types/Frontmatter';
6
6
  import { SITE } from '@config';
7
- import { Translations, Lang } from '@util/Languages';;
7
+ import { Translations, Lang } from '@util/Languages';
8
+ import { getImageInfo } from '@util/custom-markdown.mjs';
9
+ import ReadingTime from '@components/ReadingTime.astro';
8
10
 
9
11
  const accelerator = new Accelerator(SITE);
10
- const stats = new accelerator.statistics('accelerator/components/AuthorsMini.astro');
12
+ const stats = new accelerator.statistics(
13
+ 'accelerator/components/AuthorsMini.astro'
14
+ );
11
15
  stats.start();
12
16
 
13
17
  // Properties
14
18
  type Props = {
15
- lang: string;
16
- frontmatter: Frontmatter;
19
+ lang: string;
20
+ frontmatter: Frontmatter;
17
21
  };
18
22
  const { lang, frontmatter } = Astro.props satisfies Props;
19
23
 
@@ -22,29 +26,63 @@ const _ = Lang(lang);
22
26
 
23
27
  // Logic
24
28
  const authorList = accelerator.authors.forPost(frontmatter);
29
+ const dateToShow =
30
+ frontmatter.modDate == null ? frontmatter.pubDate : frontmatter.modDate;
31
+
32
+ // Get image info
33
+ const authorImage = authorList?.image?.src
34
+ ? getImageInfo(authorList.image.src, 'author-image', SITE.images.authorSize)
35
+ : null;
25
36
 
26
37
  stats.stop();
27
38
  ---
28
- {authorList.writers.length > 0 &&
29
- <div class="post-meta">
30
- <div class="author-info">
31
- <span>{ _(Translations.post.written_by) + ' ' }
32
- {authorList.mainAuthor &&
33
- <span>{ authorList.mainAuthor.frontmatter.title }</span>
34
- }{authorList.contributors.map((writer) => (
35
- <span>, </span><span>{ writer.frontmatter.title }</span>
36
- ))}<br />
37
- {frontmatter.modDate == null &&
38
- <time datetime={ frontmatter.pubDate.toString() }>
39
- { accelerator.dateFormatter.formatDate(frontmatter.pubDate, lang) }
40
- </time>
41
- }
42
- {frontmatter.modDate != null &&
43
- <time datetime={ frontmatter.modDate.toString() }>
44
- { accelerator.dateFormatter.formatDate(frontmatter.modDate, lang) }
45
- </time>
46
- }
47
- </span>
48
- </div>
49
- </div>
39
+
40
+ {
41
+ authorList.writers.length > 0 && (
42
+ <div class="post-meta mini">
43
+ <div class="author-info">
44
+ {authorImage && (
45
+ <img
46
+ srcset={authorImage.srcset}
47
+ sizes={authorImage.sizes}
48
+ src={authorImage.src}
49
+ alt={authorList?.image?.alt}
50
+ class={authorImage.class}
51
+ width={authorImage.metadata?.width}
52
+ height={authorImage.metadata?.height}
53
+ />
54
+ )}
55
+ {authorList.mainAuthor && (
56
+ <a
57
+ href={
58
+ accelerator.urlFormatter.formatAddress(
59
+ authorList.mainAuthor.url
60
+ ) + '1/'
61
+ }
62
+ class="author-name">
63
+ {authorList.mainAuthor.frontmatter.title}
64
+ </a>
65
+ )}
66
+ {authorList.contributors.map((writer) => (
67
+ <a
68
+ href={
69
+ accelerator.urlFormatter.formatAddress(writer.url) +
70
+ '1/'
71
+ }
72
+ class="contributor-name">
73
+ {writer.frontmatter.title}
74
+ </a>
75
+ ))}
76
+ <time
77
+ class="post-date"
78
+ datetime={dateToShow.toString()}
79
+ set:html={accelerator.dateFormatter.formatShortDate(
80
+ dateToShow,
81
+ lang
82
+ )}
83
+ />
84
+ <ReadingTime lang={lang} frontmatter={frontmatter} />
85
+ </div>
86
+ </div>
87
+ )
50
88
  }
@@ -1,21 +1,21 @@
1
1
  ---
2
2
  // warning: This file is overwritten by Astro Accelerator
3
3
 
4
- import { Accelerator, PostFiltering } from "astro-accelerator-utils";
5
- import type { Frontmatter } from "astro-accelerator-utils/types/Frontmatter";
6
- import { SITE } from "@config";
7
- import { Translations, Lang } from "@util/Languages";
4
+ import { Accelerator, PostFiltering } from 'astro-accelerator-utils';
5
+ import type { Frontmatter } from 'astro-accelerator-utils/types/Frontmatter';
6
+ import { SITE } from '@config';
7
+ import { Translations, Lang } from '@util/Languages';
8
8
 
9
9
  const accelerator = new Accelerator(SITE);
10
- const stats = new accelerator.statistics("accelerator/components/Header.astro");
10
+ const stats = new accelerator.statistics('accelerator/components/Header.astro');
11
11
  stats.start();
12
12
 
13
13
  // Properties
14
14
  type Props = {
15
- lang: string;
16
- frontmatter: Frontmatter;
17
- headings: { depth: number; slug: string; text: string }[];
18
- showSearch: boolean;
15
+ lang: string;
16
+ frontmatter: Frontmatter;
17
+ headings: { depth: number; slug: string; text: string }[];
18
+ showSearch: boolean;
19
19
  };
20
20
  const { lang, showSearch } = Astro.props satisfies Props;
21
21
 
@@ -29,18 +29,36 @@ stats.stop();
29
29
  ---
30
30
 
31
31
  <header class="site-header">
32
- <a href={ (SITE.subfolder ?? '') + '/' } class="site-title" translate="no">{ SITE.title }</a>
33
- <a href={searchUrl} class="header-icon">
34
- <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 20 19" stroke-width="1" fill="none" stroke-linecap="round" stroke-linejoin="round">
35
- <path d="M19.0524 16.4267L15.3727 12.7273C15.2067 12.5603 14.9815 12.4675 14.7453 12.4675H14.1437C15.1624 11.1577 15.7676 9.5102 15.7676 7.718C15.7676 3.45455 12.3316 0 8.09097 0C3.85035 0 0.414307 3.45455 0.414307 7.718C0.414307 11.9814 3.85035 15.436 8.09097 15.436C9.87358 15.436 11.5123 14.8275 12.8151 13.8033V14.4082C12.8151 14.6456 12.9073 14.872 13.0734 15.039L16.7531 18.7384C17.1 19.0872 17.661 19.0872 18.0042 18.7384L19.0487 17.6883C19.3956 17.3395 19.3956 16.7755 19.0524 16.4267ZM8.09097 12.4675C5.48164 12.4675 3.36687 10.3451 3.36687 7.718C3.36687 5.09462 5.47795 2.96846 8.09097 2.96846C10.7003 2.96846 12.8151 5.09091 12.8151 7.718C12.8151 10.3414 10.704 12.4675 8.09097 12.4675Z"></path>
36
- </svg>
37
- </a>
38
- <a href="#site-nav" data-navigationid="site-nav" class="header-icon" title={_(Translations.header.toggle_menu)}>
39
- <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
40
- <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
41
- <line x1="4" y1="6" x2="20" y2="6"></line>
42
- <line x1="4" y1="12" x2="20" y2="12"></line>
43
- <line x1="4" y1="18" x2="20" y2="18"></line>
44
- </svg>
45
- </a>
32
+ <a href={(SITE.subfolder ?? '') + '/'} class="site-title" translate="no">{SITE.title}</a>
33
+ <a href={searchUrl} class="header-icon" title={_(Translations.header.toggle_search)}>
34
+ <svg
35
+ xmlns="http://www.w3.org/2000/svg"
36
+ width="40"
37
+ height="40"
38
+ viewBox="0 0 20 19"
39
+ stroke-width="1"
40
+ fill="none"
41
+ stroke-linecap="round"
42
+ stroke-linejoin="round">
43
+ <path
44
+ d="M19.0524 16.4267L15.3727 12.7273C15.2067 12.5603 14.9815 12.4675 14.7453 12.4675H14.1437C15.1624 11.1577 15.7676 9.5102 15.7676 7.718C15.7676 3.45455 12.3316 0 8.09097 0C3.85035 0 0.414307 3.45455 0.414307 7.718C0.414307 11.9814 3.85035 15.436 8.09097 15.436C9.87358 15.436 11.5123 14.8275 12.8151 13.8033V14.4082C12.8151 14.6456 12.9073 14.872 13.0734 15.039L16.7531 18.7384C17.1 19.0872 17.661 19.0872 18.0042 18.7384L19.0487 17.6883C19.3956 17.3395 19.3956 16.7755 19.0524 16.4267ZM8.09097 12.4675C5.48164 12.4675 3.36687 10.3451 3.36687 7.718C3.36687 5.09462 5.47795 2.96846 8.09097 2.96846C10.7003 2.96846 12.8151 5.09091 12.8151 7.718C12.8151 10.3414 10.704 12.4675 8.09097 12.4675Z"
45
+ ></path>
46
+ </svg>
47
+ </a>
48
+ <a href="#site-nav" data-navigationid="site-nav" class="header-icon" title={_(Translations.header.toggle_menu)}>
49
+ <svg
50
+ xmlns="http://www.w3.org/2000/svg"
51
+ width="40"
52
+ height="40"
53
+ viewBox="0 0 24 24"
54
+ stroke-width="1.5"
55
+ fill="none"
56
+ stroke-linecap="round"
57
+ stroke-linejoin="round">
58
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
59
+ <line x1="4" y1="6" x2="20" y2="6"></line>x``
60
+ <line x1="4" y1="12" x2="20" y2="12"></line>
61
+ <line x1="4" y1="18" x2="20" y2="18"></line>
62
+ </svg>
63
+ </a>
46
64
  </header>
@@ -0,0 +1,44 @@
1
+ ---
2
+ // warning: This file is overwritten by Astro Accelerator
3
+
4
+ import { Accelerator } from 'astro-accelerator-utils';
5
+ import type { Frontmatter } from 'astro-accelerator-utils/types/Frontmatter';
6
+ import { SITE } from '@config';
7
+ import { Translations, Lang } from '@util/Languages';
8
+
9
+ const accelerator = new Accelerator(SITE);
10
+ const stats = new accelerator.statistics(
11
+ 'accelerator/components/ReadingTime.astro'
12
+ );
13
+ stats.start();
14
+
15
+ // Properties
16
+ type Props = {
17
+ lang: string;
18
+ frontmatter: Frontmatter;
19
+ };
20
+ const { lang, frontmatter } = Astro.props satisfies Props;
21
+
22
+ // Language
23
+ const _ = Lang(lang);
24
+
25
+ // Logic
26
+ const readingTimeText =
27
+ frontmatter?.readingTime === 1
28
+ ? _(Translations.post.reading_time_one_minute).replace(
29
+ '{n}',
30
+ frontmatter.readingTime
31
+ )
32
+ : _(Translations.post.reading_time_many_minutes).replace(
33
+ '{n}',
34
+ frontmatter.readingTime
35
+ );
36
+
37
+ stats.stop();
38
+ ---
39
+
40
+ {
41
+ frontmatter?.readingTime && (
42
+ <span class="reading-time" set:html={readingTimeText} />
43
+ )
44
+ }
@@ -1,116 +1,135 @@
1
1
  ---
2
2
  // warning: This file is overwritten by Astro Accelerator
3
3
 
4
- import { Accelerator, PostFiltering } from "astro-accelerator-utils";
5
- import type { Frontmatter } from "astro-accelerator-utils/types/Frontmatter";
6
- import { getImageInfo } from "@util/custom-markdown.mjs";
7
- import { SITE } from "@config";
8
- import Head from "@components/HtmlHead.astro";
9
- import Header from "@components/Header.astro";
10
- import Footer from "@components/Footer.astro";
11
- import Copyright from "@components/Copyright.astro";
12
- import SkipLinks from "@components/SkipLinks.astro";
13
- import Breadcrumbs from "@components/Breadcrumbs.astro";
14
- import Navigation from "@components/Navigation.astro";
15
- import TableOfContents from "@components/TableOfContents.astro";
16
- import Authors from "@components/Authors.astro";
17
- import Taxonomy from "@components/Taxonomy.astro";
18
- import Related from "@components/Related.astro";
4
+ import { Accelerator, PostFiltering } from 'astro-accelerator-utils';
5
+ import type { Frontmatter } from 'astro-accelerator-utils/types/Frontmatter';
6
+ import { getImageInfo } from '@util/custom-markdown.mjs';
7
+ import { SITE } from '@config';
8
+ import Head from '@components/HtmlHead.astro';
9
+ import Header from '@components/Header.astro';
10
+ import Footer from '@components/Footer.astro';
11
+ import Copyright from '@components/Copyright.astro';
12
+ import SkipLinks from '@components/SkipLinks.astro';
13
+ import Breadcrumbs from '@components/Breadcrumbs.astro';
14
+ import Navigation from '@components/Navigation.astro';
15
+ import TableOfContents from '@components/TableOfContents.astro';
16
+ import Authors from '@components/Authors.astro';
17
+ import AuthorsMini from '@components/AuthorsMini.astro';
18
+ import Taxonomy from '@components/Taxonomy.astro';
19
+ import Related from '@components/Related.astro';
19
20
 
20
21
  const accelerator = new Accelerator(SITE);
21
- const stats = new accelerator.statistics("accelerator/layouts/Default.astro");
22
+ const stats = new accelerator.statistics('accelerator/layouts/Default.astro');
22
23
  stats.start();
23
24
 
24
25
  type Props = {
25
- frontmatter: Frontmatter;
26
- headings: { depth: number; slug: string; text: string }[];
27
- breadcrumbs?: { url: string; title: string; ariaCurrent?: string }[] | null;
26
+ frontmatter: Frontmatter;
27
+ headings: { depth: number; slug: string; text: string }[];
28
+ breadcrumbs?: { url: string; title: string; ariaCurrent?: string }[] | null;
28
29
  };
29
- const { frontmatter, headings, breadcrumbs } = Astro.props satisfies Props;
30
+ const { frontmatter, headings, breadcrumbs, data } =
31
+ Astro.props satisfies Props;
30
32
 
31
33
  const lang = frontmatter.lang ?? SITE.default.lang;
32
34
  const textDirection = frontmatter.dir ?? SITE.default.dir;
33
35
 
34
36
  // Logic
35
37
  const metaImage = frontmatter.bannerImage
36
- ? getImageInfo(frontmatter.bannerImage.src, "", SITE.images.contentSize)
37
- : null;
38
+ ? getImageInfo(frontmatter.bannerImage.src, '', SITE.images.contentSize)
39
+ : null;
38
40
 
39
41
  const title = await accelerator.markdown.getInlineHtmlFrom(
40
- frontmatter.title ?? SITE.title
42
+ frontmatter.title ?? SITE.title
41
43
  );
42
44
  const subtitle = frontmatter.subtitle
43
- ? await accelerator.markdown.getInlineHtmlFrom(frontmatter.subtitle)
44
- : null;
45
+ ? await accelerator.markdown.getInlineHtmlFrom(frontmatter.subtitle)
46
+ : null;
45
47
 
46
48
  const site_url = SITE.url;
47
49
  const site_features = SITE.featureFlags;
48
50
  const search =
49
- accelerator.posts.all().filter(PostFiltering.isSearch).shift() ?? null;
51
+ accelerator.posts.all().filter(PostFiltering.isSearch).shift() ?? null;
50
52
  const searchUrl = search && accelerator.urlFormatter.formatAddress(search.url);
51
53
  const isSearchPage =
52
- accelerator.urlFormatter.formatAddress(Astro.url.pathname) === searchUrl;
54
+ accelerator.urlFormatter.formatAddress(Astro.url.pathname) === searchUrl;
53
55
  const showSearch = !isSearchPage;
54
56
 
55
57
  stats.stop();
56
58
  ---
57
59
 
58
60
  <html dir={textDirection} lang={lang} class="initial">
59
- <Head frontmatter={frontmatter} headings={headings} lang={lang} />
60
- <body>
61
- <SkipLinks frontmatter={frontmatter} headings={headings} lang={lang} />
62
- <Header
63
- frontmatter={frontmatter}
64
- headings={headings}
65
- lang={lang}
66
- showSearch={showSearch}
67
- />
68
- <Breadcrumbs
69
- frontmatter={frontmatter}
70
- headings={headings}
71
- lang={lang}
72
- breadcrumbs={breadcrumbs}
73
- />
74
- <div class="content-group">
75
- <main id="site-main">
76
- <article itemscope itemtype="https://schema.org/Article">
77
- <header>
78
- <h1 itemprop="name headline"><Fragment set:html={title} /></h1>
79
- {
80
- subtitle && (
81
- <p>
82
- <Fragment set:html={subtitle} />
83
- </p>
84
- )
85
- }
86
- </header>
87
- <div class="page-content anim-show-parent" itemprop="articleBody">
88
- <TableOfContents
89
- frontmatter={frontmatter}
90
- headings={headings}
91
- lang={lang}
61
+ <Head frontmatter={frontmatter} headings={headings} lang={lang} />
62
+ <body>
63
+ <SkipLinks frontmatter={frontmatter} headings={headings} lang={lang} />
64
+ <Header
65
+ frontmatter={frontmatter}
66
+ headings={headings}
67
+ lang={lang}
68
+ showSearch={showSearch}
69
+ />
70
+ <Breadcrumbs
71
+ frontmatter={frontmatter}
72
+ headings={headings}
73
+ lang={lang}
74
+ breadcrumbs={breadcrumbs}
75
+ />
76
+ <div class="content-group">
77
+ <main id="site-main">
78
+ <article itemscope itemtype="https://schema.org/Article">
79
+ <header>
80
+ <h1 itemprop="name headline">
81
+ <Fragment set:html={title} />
82
+ </h1>
83
+ {
84
+ subtitle && (
85
+ <p>
86
+ <Fragment set:html={subtitle} />
87
+ </p>
88
+ )
89
+ }
90
+ </header>
91
+ <div
92
+ class="page-content anim-show-parent"
93
+ itemprop="articleBody">
94
+ <AuthorsMini frontmatter={frontmatter} lang={lang} />
95
+ <TableOfContents
96
+ frontmatter={frontmatter}
97
+ headings={headings}
98
+ lang={lang}
99
+ />
100
+ <slot />
101
+ <Authors frontmatter={frontmatter} lang={lang} />
102
+ <Taxonomy frontmatter={frontmatter} lang={lang} />
103
+ <Related
104
+ frontmatter={frontmatter}
105
+ headings={headings}
106
+ lang={lang}
107
+ />
108
+ {
109
+ metaImage && (
110
+ <meta
111
+ itemprop="image"
112
+ content={metaImage.src}
113
+ />
114
+ )
115
+ }
116
+ </div>
117
+ </article>
118
+ </main>
119
+ <Navigation lang={lang} />
120
+ </div>
121
+ <Footer frontmatter={frontmatter} headings={headings} lang={lang}>
122
+ <Copyright
123
+ frontmatter={frontmatter}
124
+ headings={headings}
125
+ lang={lang}
92
126
  />
93
- <slot />
94
- <Authors frontmatter={frontmatter} lang={lang} />
95
- <Taxonomy frontmatter={frontmatter} lang={lang} />
96
- <Related
97
- frontmatter={frontmatter}
98
- headings={headings}
99
- lang={lang}
100
- />
101
- {metaImage && <meta itemprop="image" content={metaImage.src} />}
102
- </div>
103
- </article>
104
- </main>
105
- <Navigation lang={lang} />
106
- </div>
107
- <Footer frontmatter={frontmatter} headings={headings} lang={lang}>
108
- <Copyright frontmatter={frontmatter} headings={headings} lang={lang} />
109
- </Footer>
110
- <script define:vars={{ site_url, site_features }}>
111
- window.site_url = site_url;
112
- window.site_features = site_features;
113
- </script>
114
- <script src={SITE.subfolder + "/js/main.js"} type="module" async></script>
115
- </body>
127
+ </Footer>
128
+ <script define:vars={{ site_url, site_features }}>
129
+ window.site_url = site_url;
130
+ window.site_features = site_features;
131
+ </script>
132
+ <script src={SITE.subfolder + '/js/main.js'} type="module" async
133
+ ></script>
134
+ </body>
116
135
  </html>
@@ -62,6 +62,12 @@
62
62
  },
63
63
  "last_modified": {
64
64
  "en": "Revised"
65
+ },
66
+ "reading_time_one_minute": {
67
+ "en": "{n} min read"
68
+ },
69
+ "reading_time_many_minutes": {
70
+ "en": "{n} min read"
65
71
  }
66
72
  },
67
73
  "author": {
@@ -0,0 +1,18 @@
1
+ /** @format */
2
+
3
+ import { toString } from 'mdast-util-to-string';
4
+
5
+ /**
6
+ * Remark plugin for calculating reading time
7
+ */
8
+ export function readingTime() {
9
+ return function (tree, { data }) {
10
+ const wordsPerMinute = 200;
11
+ const textOnPage = toString(tree);
12
+ const readingTime = Math.ceil(
13
+ textOnPage.split(' ').length / wordsPerMinute
14
+ );
15
+
16
+ data.astro.frontmatter.readingTime = readingTime;
17
+ };
18
+ }