astro-accelerator 5.9.21 → 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.21",
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
  }
@@ -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
+ }