This commit is contained in:
Adam Matthiesen 2024-01-23 16:17:23 -08:00
parent d56c9b962f
commit 9116e28c2e
31 changed files with 249 additions and 1119 deletions

View File

@ -1 +1 @@
export * as Utils from './src/utils'
export * as Utils from './src/utils';

View File

@ -41,8 +41,16 @@
"./archives/[...page].astro": "./src/routes/archives/[...page].astro"
},
"scripts": { },
"devDependencies": {
"@matthiesenxyz/astro-ghostcms": "*",
"@astrojs/check": "^0.4.1",
"typescript": "^5.3.3"
},
"peerDependencies": {
"astro": "^4.2.1"
},
"dependencies": {
"@matthiesenxyz/astro-ghostcms": "^2.1.8",
"@matthiesenxyz/astro-ghostcms": "^3.0.0",
"tiny-invariant": "^1.3.1",
"astro-font": "^0.0.77"
}

View File

@ -1,8 +1,8 @@
---
import { getGhostImgPath } from "../utils";
import type { Settings, PostOrPage } from "@matthiesenxyz/astro-ghostcms/api";
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
post: PostOrPage;
post: Post;
settings: Settings;
};
const { post, settings } = Astro.props as Props;

View File

@ -0,0 +1,77 @@
---
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
import { ViewTransitions } from 'astro:transitions';
export type Props = {
title: string;
description: string;
permalink?: string;
image?: string;
settings: Settings;
};
const { description, permalink, image, settings, title } = Astro.props as Props;
---
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title}</title>
<ViewTransitions />
<meta name="title" content={title} />
{description && <meta name="description" content={description} />}
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href={settings.icon} />
<link rel="shortcut icon" type="image/png" sizes="16x16" href={settings.icon} />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="msapplication-config" content="/browserconfig.xml" />
<meta name="theme-color" content="#ffffff" />
<!-- Open Graph Tags (Facebook) -->
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
{permalink && <meta property="og:url" content={permalink} />}
{description && <meta property="og:description" content={description} />}
{image && <meta property="og:image" content={image} />}
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:title" content={title} />
{permalink && <meta property="twitter:url" content={permalink} />}
{description && <meta property="twitter:description" content={description} />}
{image && <meta property="twitter:image" content={image} />}
<!-- Link to the global style, or the file that imports constructs -->
<link
rel="preload"
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
as="style"
/>
<link
rel="preload"
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"
as="script"
/>
<link
rel="preload stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
crossorigin
/>
<script
async
defer
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"
>
</script>

View File

@ -9,10 +9,10 @@ const { settings } = Astro.props as Props;
<footer class="site-footer outer">
<div class="inner">
<section class="copyright">
<a href="/">{settings.title}</a> &copy; 2024
<a href="/">{settings.title}</a> &copy; 2021
</section>
<nav class="site-footer-nav">
{settings.secondary_navigation && settings.secondary_navigation.length > 0 && (
{settings.secondary_navigation.length > 0 && (
<ul class="nav secondary">
{settings.secondary_navigation.map(({ label, url }) => (
<li class="">

View File

@ -1,4 +1,5 @@
---
import { twitter, facebook } from "@matthiesenxyz/astro-ghostcms/api";
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
settings: Settings;
@ -24,7 +25,7 @@ const { settings } = Astro.props as Props;
</div>
<div class="gh-head-menu">
<ul class="nav">
{settings.navigation && settings.navigation.map(({ label, url }) => (
{settings.navigation.map(({ label, url }) => (
<li class="nav__item">
<a href={url}>{label}</a>
</li>
@ -36,7 +37,7 @@ const { settings } = Astro.props as Props;
{settings.facebook && (
<a
class="gh-social-facebook"
href={"https://facebook.com/" + settings.facebook}
href={facebook(settings.facebook)}
title="Facebook"
target="_blank"
rel="noopener"
@ -49,7 +50,7 @@ const { settings } = Astro.props as Props;
{settings.twitter && (
<a
class="gh-social-twitter"
href={"https://twitter.com/" + settings.twitter}
href={twitter(settings.twitter)}
title="Twitter"
target="_blank"
rel="noopener"

View File

@ -1,6 +1,6 @@
---
import Header from "./Header.astro";
import Footer from "./Footer.astro";
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
settings: Settings;

View File

@ -1,8 +1,8 @@
---
import FeatureImage from "./FeatureImage.astro";
import type { Settings, PostOrPage } from "@matthiesenxyz/astro-ghostcms/api";
import FeatureImage from "../components/FeatureImage.astro";
import type { Settings, Page } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
page: PostOrPage;
page: Page;
settings: Settings;
pageClass?: string;
};

View File

@ -1,13 +1,13 @@
---
import PostHero from "./PostHero.astro";
import PostFooter from "./PostFooter.astro";
import PostHero from "../components/PostHero.astro";
import PostFooter from "../components/PostFooter.astro";
import invariant from "tiny-invariant";
import type {PostOrPage, PostsOrPages, Settings } from "@matthiesenxyz/astro-ghostcms/api";
import type {Post, Settings } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
post: PostOrPage;
post: Post;
settings: Settings;
postClass?: string;
posts: PostsOrPages;
posts: Post[];
};
const { post, settings, postClass, posts } = Astro.props as Props;
invariant(settings, "Settings not found");

View File

@ -1,15 +1,15 @@
---
import PostPreview from "./PostPreview.astro";
import type { Settings, PostOrPage, PostsOrPages } from "@matthiesenxyz/astro-ghostcms/api";
import PostPreview from "../components/PostPreview.astro";
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
post: PostOrPage;
post: Post;
settings: Settings;
posts: PostsOrPages;
posts: Post[];
};
const { post, settings, posts } = Astro.props as Props;
---
<section class="footer-cta">
<!--section class="footer-cta">
<div class="inner">
<h2>Sign up for more like this.</h2>
<a class="footer-cta-button" href="#/portal">
@ -17,14 +17,14 @@ const { post, settings, posts } = Astro.props as Props;
<span>Subscribe</span>
</a>
</div>
</section>
</section-->
<aside class="read-more-wrap">
<div class="read-more inner">
{posts
.filter((p: PostOrPage) => p.id !== post.id)
.filter((p: Post) => p.id !== post.id)
.slice(0, 3)
.map((post: PostOrPage) => <PostPreview post={post} settings={settings} />)}
.map((post: Post) => <PostPreview post={post} settings={settings} />)}
</div>
</aside>

View File

@ -1,10 +1,10 @@
---
import FeatureImage from "./FeatureImage.astro";
import AuthorList from "./AuthorList.astro";
import FeatureImage from "../components/FeatureImage.astro";
import AuthorList from "../components/AuthorList.astro";
import { formatDate } from "../utils";
import type { Settings, PostOrPage } from "@matthiesenxyz/astro-ghostcms/api";
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
post: PostOrPage;
post: Post;
settings: Settings;
};
const { post, settings } = Astro.props as Props;
@ -29,11 +29,9 @@ const { post, settings } = Astro.props as Props;
</h4>
)}
<div class="byline-meta-content">
{post.created_at && (
<time class="byline-meta-date" datetime={formatDate(post.created_at)}
<time class="byline-meta-date" datetime={formatDate(post.created_at)}
>{formatDate(post.created_at)}
</time>
)}
<span class="byline-reading-time"
><span class="bull">&bull;</span>
{post.reading_time} min read

View File

@ -1,9 +1,9 @@
---
import { getGhostImgPath, formatDate } from "../utils";
import AuthorList from "./AuthorList.astro";
import type { Settings, PostOrPage, Tag } from "@matthiesenxyz/astro-ghostcms/api";
import AuthorList from "../components/AuthorList.astro";
import type { Settings, Post, Tag } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
post: PostOrPage;
post: Post;
settings: Settings;
index?: number;
isHome?: boolean;
@ -18,7 +18,7 @@ const { post, settings, index, isHome = false } = Astro.props as Props;
isHome && post.feature_image && index == 0 ? "post-card-large" : ""
}`}
>
<a class="post-card-image-link" href={`/${post.slug}`} data-astro-reload>
<a class="post-card-image-link" href={`/${post.slug}`}>
<img
class="post-card-image"
srcset={`
@ -35,7 +35,7 @@ const { post, settings, index, isHome = false } = Astro.props as Props;
/>
</a>
<div class="post-card-content">
<a class="post-card-content-link" href={`/${post.slug}`} data-astro-reload>
<a class="post-card-content-link" href={`/${post.slug}`}>
<header class="post-card-header">
{post.primary_tag && (
<div class="post-card-primary-tag">{post.primary_tag.name}</div>
@ -52,9 +52,9 @@ const { post, settings, index, isHome = false } = Astro.props as Props;
<div class="post-card-byline-content">
<span>{post.primary_author?.name ?? ""}</span>
<span class="post-card-byline-date"
>{post.created_at && (<time datetime={formatDate(post.created_at)}
><time datetime={formatDate(post.created_at)}
>{formatDate(post.created_at)}
</time>)}
</time>
<span class="bull">&bull;</span>
{post.reading_time} min read
</span>

View File

@ -1,8 +1,8 @@
---
import PostPreview from "./PostPreview.astro";
import type { Settings, PostOrPage } from "@matthiesenxyz/astro-ghostcms/api";
import PostPreview from "../components/PostPreview.astro";
import type { Settings, Post } from "@matthiesenxyz/astro-ghostcms/api";
export type Props = {
posts: PostOrPage[];
posts: Post[];
settings: Settings;
isHome?: boolean;
};
@ -10,7 +10,7 @@ const { posts, settings, isHome = false } = Astro.props as Props;
---
<div class="post-feed">
{posts.map((post: PostOrPage, index: number) => (
{posts.map((post: Post, index: number) => (
<PostPreview
post={post}
index={index}

View File

@ -1,95 +1,29 @@
---
import type { Settings } from "@matthiesenxyz/astro-ghostcms/api";
import { AstroFont } from "astro-font";
import BaseHead from "../components/BaseHead.astro";
import MainLayout from "../components/MainLayout.astro";
import { ViewTransitions } from "astro:transitions";
export type Props = {
content?: {
title: string|undefined;
description: string|undefined;
title: string;
description: string;
};
bodyClass?: string;
permalink?: string;
image?: string;
settings: Settings;
};
const { permalink, image, content, settings, bodyClass = "" } = Astro.props as Props;
const { content, settings, bodyClass = "" } = Astro.props as Props;
const ghostAccentColor = settings.accent_color;
console.log(settings.accent_color)
---
<html lang="en" data-color-scheme="light">
<head>
<AstroFont
config={[
{
src: [],
name: "Inter",
preload: false,
display: "swap",
selector: "html",
fallback: "sans-serif",
googleFontsURL: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap',
},
]}
<BaseHead
title={content?.title
? `${settings.title} | ${content.title}`
: settings.title}
description={content?.description ?? "Description"}
settings={settings}
/>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{content?.title ? `${content.title} | ${settings.title}` : "" }</title>
<ViewTransitions />
<meta name="title" content={settings.title} />
{content?.description && <meta name="description" content={content?.description} />}
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href={settings.icon} />
<link rel="shortcut icon" type="image/png" sizes="16x16" href={settings.icon} />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="msapplication-config" content="/browserconfig.xml" />
<meta name="theme-color" content="#ffffff" />
<!-- Open Graph Tags (Facebook) -->
<meta property="og:type" content="website" />
<meta property="og:title" content={settings.title} />
{permalink && <meta property="og:url" content={permalink} />}
{content?.description && <meta property="og:description" content={content?.description} />}
{image && <meta property="og:image" content={image} />}
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:title" content={settings.title} />
{permalink && <meta property="twitter:url" content={permalink} />}
{content?.description && <meta property="twitter:description" content={content?.description} />}
{image && <meta property="twitter:image" content={image} />}
<!-- Link to the global style, or the file that imports constructs -->
<link
rel="preload"
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
as="style"
/>
<link
rel="preload"
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"
as="script"
/>
<link
rel="preload stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
crossorigin
/>
<script
async
defer
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"
>
</script>
</head>
<body class={bodyClass}>
<MainLayout settings={settings}>

View File

@ -3,11 +3,11 @@ import type { InferGetStaticPropsType } from 'astro';
import DefaultPageLayout from "../layouts/default.astro";
import Page from "../components/Page.astro";
import Post from "../components/Post.astro";
import { getGhostSettings, getGhostPages, getGhostPosts } from "@matthiesenxyz/astro-ghostcms/api";
import { getSettings, getAllPages, getAllPosts } from "@matthiesenxyz/astro-ghostcms/api";
import invariant from 'tiny-invariant';
export async function getStaticPaths() {
const [posts, pages, settings] = await Promise.all([getGhostPosts(), await getGhostPages(), await getGhostSettings()]);
const [posts, pages, settings] = await Promise.all([getAllPosts(), await getAllPages(), await getSettings()]);
const allPosts = [...posts, ...pages];
return allPosts.map((post) => ({
params: { slug: post.slug },

View File

@ -1,24 +1,25 @@
---
import type { GetStaticPathsOptions, Page } from 'astro';
import type { Post } from "@matthiesenxyz/astro-ghostcms/api";
import invariant from "tiny-invariant";
import DefaultPageLayout from "../../layouts/default.astro";
import PostPreviewList from "../../components/PostPreviewList.astro";
import HeroContent from "../../components/HeroContent.astro";
import Paginator from "../../components/Paginator.astro";
import { getGhostSettings, getGhostPosts, type PostOrPage } from "@matthiesenxyz/astro-ghostcms/api";
import DefaultPageLayout from "../layouts/default.astro";
import PostPreviewList from "../components/PostPreviewList.astro";
import HeroContent from "../components/HeroContent.astro";
import Paginator from "../components/Paginator.astro";
import { getSettings, getAllPosts } from "@matthiesenxyz/astro-ghostcms/api";
export async function getStaticPaths({ paginate }:GetStaticPathsOptions) {
const posts = await getGhostPosts();
const posts = await getAllPosts();
return paginate(posts, {
pageSize: 5,
});
}
export type Props = {
page: Page<PostOrPage>
page: Page<Post>
};
const settings = await getGhostSettings();
const settings = await getSettings();
invariant(settings, "Settings are required");
const title = settings.title;

View File

@ -1,14 +1,15 @@
---
import type { InferGetStaticParamsType, InferGetStaticPropsType } from 'astro';
import DefaultPageLayout from "../../layouts/default.astro";
import PostPreviewList from "../../components/PostPreviewList.astro";
import { getGhostPosts, getGhostAuthors, getGhostSettings } from "@matthiesenxyz/astro-ghostcms/api";
import DefaultPageLayout from "../layouts/default.astro";
import PostPreviewList from "../components/PostPreviewList.astro";
import { getAllPosts, getAllAuthors, getSettings } from "@matthiesenxyz/astro-ghostcms/api";
import { twitter, facebook } from "@matthiesenxyz/astro-ghostcms/api";
import invariant from "tiny-invariant";
export async function getStaticPaths() {
const posts = await getGhostPosts();
const authors = await getGhostAuthors();
const settings = await getGhostSettings();
const posts = await getAllPosts();
const { authors } = await getAllAuthors();
const settings = await getSettings();
return authors.map((author) => {
const filteredPosts = posts.filter((post) =>
@ -67,7 +68,7 @@ const description = `All of the articles we've posted and linked so far under th
? author.bio
: author.count?.posts || 0 > 0
? `${author.count?.posts} Posts`
: ""}
: "No posts"}
</p>
<div class="author-profile-meta">
@ -91,7 +92,7 @@ const description = `All of the articles we've posted and linked so far under th
<span>
<a
class="author-profile-social-link"
href={author.twitter}
href={twitter(author.twitter)}
target="_blank"
rel="noopener"
>
@ -103,7 +104,7 @@ const description = `All of the articles we've posted and linked so far under th
<span>
<a
class="author-profile-social-link"
href={author.facebook}
href={facebook(author.facebook)}
target="_blank"
rel="noopener"
>

View File

@ -1,13 +1,13 @@
---
import DefaultPageLayout from "../layouts/default.astro";
import AuthorCard from "../components/AuthorCard.astro";
import { getGhostAuthors, getGhostSettings } from "@matthiesenxyz/astro-ghostcms/api";
import { getAllAuthors, getSettings } from "@matthiesenxyz/astro-ghostcms/api";
import invariant from "tiny-invariant";
let title = "All Authors";
let description = "All the authors";
const authors = await getGhostAuthors();
const settings = await getGhostSettings();
const { authors } = await getAllAuthors();
const settings = await getSettings();
invariant(settings, 'Settings not found');
---

View File

@ -2,12 +2,11 @@
import DefaultPageLayout from "../layouts/default.astro";
import PostPreviewList from "../components/PostPreviewList.astro";
import HeroContent from "../components/HeroContent.astro";
import { getGhostPosts, getGhostSettings } from "@matthiesenxyz/astro-ghostcms/api";
import invariant from "tiny-invariant";
const posts = await getGhostPosts();
const settings = await getGhostSettings();
invariant(settings, "Settings not found");
import { getPosts, getSettings } from "@matthiesenxyz/astro-ghostcms/api";
import invariant from 'tiny-invariant';
const { posts } = await getPosts();
const settings = await getSettings();
invariant(settings, 'Settings not found');
const title = settings.title;
const description = settings.description;
---

View File

@ -1,15 +1,15 @@
---
import type { InferGetStaticParamsType, InferGetStaticPropsType } from 'astro';
import DefaultPageLayout from "../../layouts/default.astro";
import PostPreview from "../../components/PostPreview.astro";
import { getGhostPosts, getGhostTags, getGhostSettings } from "@matthiesenxyz/astro-ghostcms/api";
import DefaultPageLayout from "../layouts/default.astro";
import PostPreview from "../components/PostPreview.astro";
import { getAllPosts, getAllTags, getSettings } from "@matthiesenxyz/astro-ghostcms/api";
import { getGhostImgPath } from "../../utils";
import invariant from "tiny-invariant";
export async function getStaticPaths() {
const posts = await getGhostPosts();
const tags = await getGhostTags();
const settings = await getGhostSettings();
const posts = await getAllPosts();
const { tags } = await getAllTags();
const settings = await getSettings();
return tags.map((tag) => {
const filteredPosts = posts.filter((post) =>

View File

@ -1,15 +1,15 @@
---
import DefaultPageLayout from "../layouts/default.astro";
import TagCard from "../components/TagCard.astro";
import { getGhostSettings, getGhostTags } from "@matthiesenxyz/astro-ghostcms/api";
import { getSettings, getAllTags } from "@matthiesenxyz/astro-ghostcms/api";
import invariant from 'tiny-invariant';
let title = "All Tags";
let description = "All the tags used so far...";
const tags = await getGhostTags();
const settings = await getGhostSettings();
const { tags } = await getAllTags();
const settings = await getSettings();
invariant(settings, "Settings not found");
---
@ -24,7 +24,9 @@ invariant(settings, "Settings not found");
</div>
<div class="tag-feed">
{
tags.map((tag) => (
tags
.filter((tag) => tag.slug && !tag.slug.startsWith("hash-"))
.map((tag) => (
<article class="post-card ">
<TagCard tag={tag} settings={settings} />
</article>

View File

@ -111,6 +111,8 @@ img {
}
html {
box-sizing: border-box;
font-family: sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}

View File

@ -7,7 +7,7 @@ export const getGhostImgPath = (
if (!imgUrl.startsWith(baseUrl)) {
return imgUrl;
}
const relativePath = imgUrl.substring(`${baseUrl}/content/images`.length);
const relativePath = imgUrl.substring(`${baseUrl}content/images`.length);
const cleanedBaseUrl = baseUrl.replace(/\/~/, "");
if (width && width > 0) {
return `${cleanedBaseUrl}content/images/size/w${width}/${relativePath}`;

View File

@ -37,7 +37,6 @@
"email": "issues@astro-ghostcms.xyz"
},
"main": "index.ts",
"types": "src/api/tryghost-content-api.d.ts",
"files": [
"src",
"index.ts"
@ -45,15 +44,9 @@
"exports": {
".": "./index.ts",
"./api": "./src/api/index.ts",
"./api-core": "./src/api/content-api",
"./404.astro": "./src/default-routes/404/404.astro",
"./rss.xml.js": "./src/default-routes/rss.xml.js",
"./index.astro": "./src/theme/routes/index.astro",
"./[slug].astro": "./src/theme/routes/[slug].astro",
"./tags.astro": "./src/theme/routes/tags.astro",
"./authors.astro": "./src/theme/routes/authors.astro",
"./tag/[slug].astro": "./src/theme/routes/tag/[slug].astro",
"./author/[slug].astro": "./src/theme/routes/author/[slug].astro",
"./archives/[...page].astro": "./src/theme/routes/archives/[...page].astro"
"./rss.xml.js": "./src/default-routes/rss.xml.js"
},
"scripts": {
"dev": "astro dev",
@ -67,6 +60,7 @@
"astro": "^4.2.1"
},
"devDependencies": {
"@matthiesenxyz/astro-ghostcms-theme-default": "workspace:*",
"@astrojs/check": "^0.3.4",
"@ts-ghost/core-api": "*",
"@ts-ghost/tsconfig": "*",
@ -81,21 +75,21 @@
"prettier-plugin-astro": "^0.13.0",
"tsup": "^8.0.0",
"typescript": "^5.3.3",
"vite": "^4.5.2",
"vite-tsconfig-paths": "^4.2.2",
"vitest": "^1.1.0",
"vitest-fetch-mock": "^0.2.2",
"zod": "^3.22.4",
"zod-validation-error": "^3.0.0"
"vitest-fetch-mock": "^0.2.2"
},
"dependencies": {
"@matthiesenxyz/astro-ghostcms-theme-default": "workspace:*",
"@matthiesenxyz/astro-ghostcms-theme-default": "0.0.1",
"@astrojs/rss": "^4.0.2",
"@astrojs/sitemap": "^3.0.5",
"@ts-ghost/core-api": "^5.1.2",
"astro-robots-txt": "^1.0.0",
"axios": "^1.6.5",
"sass": "^1.70.0",
"tiny-invariant": "^1.3.1"
"tiny-invariant": "^1.3.1",
"vite": "^4.5.2",
"vite-tsconfig-paths": "^4.2.2",
"zod": "^3.22.4",
"zod-validation-error": "^3.0.0"
}
}

View File

@ -1 +1,4 @@
export * from './content-api';
export * from './schemas';
export type { InferFetcherDataShape, InferResponseDataShape, BrowseParams } from "@ts-ghost/core-api";

View File

@ -1,5 +1,2 @@
export * from './api-functions';
export * from './content-api';
export * from './content-api/schemas'
export type { InferFetcherDataShape, InferResponseDataShape, BrowseParams } from "@ts-ghost/core-api";
export * from './content-api/schemas';

View File

@ -1,81 +0,0 @@
// IMPORT Ghost Types & Content-API
import type {
PostOrPage, PostsOrPages, Authors,
Tag, Tags, ArrayOrValue, IncludeParam,
LimitParam, Settings, Nullable
} from './tryghost-content-api';
import GhostContentAPI from './tryghost-content-api';
// LOAD ENVIRONMENT VARIABLES
import { loadEnv } from 'vite';
const {CONTENT_API_KEY, CONTENT_API_URL} = loadEnv(
'all',
process.cwd(),
'CONTENT_'
);
let key = CONTENT_API_KEY;
let url = CONTENT_API_URL;
// SETUP API
const version = "v5.0";
const api = GhostContentAPI({ key, url, version });
// SET Include params
const include:ArrayOrValue<IncludeParam> = ['authors', 'tags'];
/** Get Posts (General "ALL") */
export const getGhostPosts = async () => {
const ghostPosts:PostsOrPages = await api.posts.browse({include,filter:'visibility:public'})
return ghostPosts; };
/** Get Posts (Recent "setLimit?") */
export const getGhostRecentPosts = async (setLimit?:ArrayOrValue<LimitParam>) => {
const ghostRecentPosts:PostsOrPages = await api.posts.browse({limit:setLimit?setLimit:"6",include,filter:'visibility:public'});
return ghostRecentPosts; };
/** Get Posts (Featured "setLimit?") */
export const getGhostFeaturedPosts = async (setLimit?:ArrayOrValue<LimitParam>) => {
const ghostFeaturedPosts:PostsOrPages = await api.posts.browse({limit:setLimit?setLimit:"1",include,filter:'featured:true'});
return ghostFeaturedPosts; };
/** Get Post (By Slug) */
export const getGhostPostbySlug = async (slug:Nullable<string>) => {
const ghostPostbySlug:PostOrPage = await api.posts.read({slug},{include});
return ghostPostbySlug; };
/** Get Post (By Tag) */
export const getGhostPostsbyTag = async (slug:Nullable<string>) => {
const ghostPostsbyTag:PostsOrPages = await api.posts.browse({filter:`tag:${slug}`,include});
return ghostPostsbyTag; };
/** Get Tags (General "ALL") */
export const getGhostTags = async () => {
const ghostTags:Tags = await api.tags.browse({include:`count.posts`});
return ghostTags; };
/** Get Tag (By Slug) */
export const getGhostTagbySlug = async (slug:Nullable<string>) => {
const ghostTagbySlug:Tag = await api.tags.read({slug},{include:`count.posts`});
return ghostTagbySlug; };
/** Get Authors (General "ALL") */
export const getGhostAuthors = async () => {
const ghostAuthors:Authors = await api.authors.browse({include:`count.posts`});
return ghostAuthors; };
/** Get Pages (ALL) */
export const getGhostPages = async () => {
const ghostPages:PostsOrPages = await api.pages.browse();
return ghostPages; };
/** Get Page (by Slug) */
export const getGhostPage = async (slug:Nullable<string>) => {
const ghostPage:PostOrPage = await api.pages.read({slug});
return ghostPage; };
/** Get Settings */
export const getGhostSettings = async () => {
const ghostSettings:Settings = await api.settings.browse();
return ghostSettings; };

View File

@ -1,22 +0,0 @@
// FUNCTION EXPORTS
export {
getGhostPosts, getGhostRecentPosts, getGhostFeaturedPosts,
getGhostPostbySlug, getGhostPostsbyTag, getGhostTags,
getGhostTagbySlug, getGhostAuthors, getGhostPages,
getGhostPage, getGhostSettings
} from './functions';
// TYPE EXPORTS
export type {
PostOrPage, ArrayOrValue, Author,
Authors, BrowseFunction, CodeInjection,
Excerpt, Facebook, FieldParam,
FilterParam, FormatParam, GhostAPI,
GhostContentAPIOptions, GhostData, GhostError,
Identification, IncludeParam, LimitParam,
Metadata, Nullable, OrderParam,
PageParam, Pagination, Params,
PostsOrPages, ReadFunction, Settings,
SettingsResponse, SocialMedia, Tag,
TagVisibility, Tags, Twitter
} from './tryghost-content-api';

View File

@ -1,253 +0,0 @@
export type ArrayOrValue<T> = T | T[];
export type Nullable<T> = T | null;
export interface Pagination {
page: number;
limit: number;
pages: number;
total: number;
next: Nullable<number>;
prev: Nullable<number>;
}
export interface Identification {
slug: string;
id: string;
}
export interface Metadata {
meta_title?: Nullable<string> | undefined;
meta_description?: Nullable<string> | undefined;
}
export interface Excerpt {
excerpt?: string | undefined;
custom_excerpt?: string | undefined;
}
export interface CodeInjection {
codeinjection_head?: Nullable<string> | undefined;
codeinjection_foot?: Nullable<string> | undefined;
}
/** Metadata for Facebook */
export interface Facebook {
og_image?: Nullable<string> | undefined;
og_title?: Nullable<string> | undefined;
og_description?: Nullable<string> | undefined;
}
export interface Twitter {
twitter_image?: Nullable<string> | undefined;
twitter_title?: Nullable<string> | undefined;
twitter_description?: Nullable<string> | undefined;
}
export interface SocialMedia extends Facebook, Twitter {
}
export interface Settings extends Metadata, CodeInjection, SocialMedia {
title?: string | undefined;
description?: string | undefined;
logo?: string | undefined;
icon?: string | undefined;
accent_color?: Nullable<string> | undefined;
cover_image?: string | undefined;
facebook?: string | undefined;
twitter?: string | undefined;
lang?: string | undefined;
timezone?: string | undefined;
ghost_head?: Nullable<string> | undefined;
ghost_foot?: Nullable<string> | undefined;
navigation?:
| Array<{
label: string;
url: string;
}>
| undefined;
secondary_navigation?:
| Array<{
label: string;
url: string;
}>
| undefined;
url: string;
}
export interface Author extends Identification, Metadata {
name?: string | undefined;
profile_image?: Nullable<string> | undefined;
cover_image?: Nullable<string> | undefined;
bio?: Nullable<string> | undefined;
website?: Nullable<string> | undefined;
location?: Nullable<string> | undefined;
facebook?: Nullable<string> | undefined;
twitter?: Nullable<string> | undefined;
url?: Nullable<string> | undefined;
count?: {
posts: number;
} | undefined;
}
export type TagVisibility = "public" | "internal";
export interface Tag extends Identification, Metadata, SocialMedia {
name?: string | undefined;
description?: Nullable<string> | undefined;
feature_image?: Nullable<string> | undefined;
visibility?: TagVisibility | undefined;
url?: string | undefined;
canonical_url?: Nullable<string> | undefined;
accent_color?: Nullable<string> | undefined;
count?: {
posts: number;
} | undefined;
}
export interface PostOrPage extends Identification, Excerpt, CodeInjection, Metadata, SocialMedia {
// Identification
uuid?: string | undefined;
comment_id?: string | undefined;
featured?: boolean | undefined;
// Post or Page
title?: string | undefined;
html?: Nullable<string> | undefined;
plaintext?: Nullable<string> | undefined;
// Image
feature_image?: Nullable<string> | undefined;
feature_image_alt?: Nullable<string> | undefined;
feature_image_caption?: Nullable<string> | undefined;
// Dates
created_at?: string | undefined;
updated_at?: Nullable<string> | undefined;
published_at?: Nullable<string> | undefined;
// Custom Template for posts and pages
custom_template?: Nullable<string> | undefined;
// Post or Page
page?: boolean | undefined;
// Reading time
reading_time?: number | undefined;
// Tags - Only shown when using Include param
tags?: Tag[] | undefined;
primary_tag?: Nullable<Tag> | undefined;
// Authors - Only shown when using Include Param
authors?: Author[] | undefined;
primary_author?: Nullable<Author> | undefined;
url?: string | undefined;
canonical_url?: Nullable<string> | undefined;
}
export type GhostData = PostOrPage | Author | Tag | Settings;
export type IncludeParam = "authors" | "tags" | "count.posts";
export type FieldParam = string;
export type FormatParam = "html" | "plaintext";
export type FilterParam = string;
export type LimitParam = number | string;
export type PageParam = number;
export type OrderParam = string;
export interface Params {
include?: ArrayOrValue<IncludeParam> | undefined;
fields?: ArrayOrValue<FieldParam> | undefined;
formats?: ArrayOrValue<FormatParam> | undefined;
filter?: ArrayOrValue<FilterParam> | undefined;
limit?: ArrayOrValue<LimitParam> | undefined;
page?: ArrayOrValue<PageParam> | undefined;
order?: ArrayOrValue<OrderParam> | undefined;
}
export interface BrowseFunction<T> {
(options?: Params, memberToken?: Nullable<string>): Promise<T>;
}
export interface ReadFunction<T> {
(
data: { id: Nullable<string> } | { slug: Nullable<string> },
options?: Params,
memberToken?: Nullable<string>,
): Promise<T>;
}
interface BrowseResults<T> extends Array<T> {
meta: { pagination: Pagination };
}
export interface PostsOrPages extends BrowseResults<PostOrPage> {
}
export interface Authors extends BrowseResults<Author> {
}
export interface Tags extends BrowseResults<Tag> {
}
export interface SettingsResponse extends Settings {
meta: any;
}
export interface GhostError {
errors: Array<{
message: string;
errorType: string;
}>;
}
export interface GhostContentAPIOptions {
url: string;
/**
* Version of GhostContentAPI
*
* Supported Versions: 'v2', 'v3', 'v4', 'v5.0', 'canary'
*/
version: "v2" | "v3" | "v4" | "v5.0" | "canary";
key: string;
/** @deprecated since version v2 */
host?: string | undefined;
/** @default "ghost" */
ghostPath?: string | undefined;
}
export interface GhostAPI {
posts: {
browse: BrowseFunction<PostsOrPages>;
read: ReadFunction<PostOrPage>;
};
authors: {
browse: BrowseFunction<Authors>;
read: ReadFunction<Author>;
};
tags: {
browse: BrowseFunction<Tags>;
read: ReadFunction<Tag>;
};
pages: {
browse: BrowseFunction<PostsOrPages>;
read: ReadFunction<PostOrPage>;
};
settings: {
browse: BrowseFunction<SettingsResponse>;
};
}
declare var GhostContentAPI: {
(options: GhostContentAPIOptions): GhostAPI;
new(options: GhostContentAPIOptions): GhostAPI;
};
export default GhostContentAPI;

View File

@ -1,294 +0,0 @@
'use strict';
import axios from 'axios';
var name$1 = "@tryghost/content-api";
var version = "1.11.20";
var repository = "https://github.com/TryGhost/SDK/tree/main/packages/content-api";
var author = "Ghost Foundation";
var license = "MIT";
var main = "cjs/content-api.js";
var unpkg = "umd/content-api.min.js";
var module$1 = "es/content-api.js";
var source = "lib/content-api.js";
var files = [
"LICENSE",
"README.md",
"cjs/",
"lib/",
"umd/",
"es/"
];
var scripts = {
dev: "echo \"Implement me!\"",
pretest: "yarn build",
test: "NODE_ENV=testing c8 --all --reporter text --reporter cobertura mocha './test/**/*.test.js'",
build: "rollup -c",
lint: "eslint . --ext .js --cache",
prepare: "NODE_ENV=production yarn build",
posttest: "yarn lint"
};
var publishConfig = {
access: "public"
};
var devDependencies = {
"@babel/core": "7.23.3",
"@babel/polyfill": "7.12.1",
"@babel/preset-env": "7.23.3",
"@rollup/plugin-json": "6.0.1",
c8: "8.0.1",
"core-js": "3.33.2",
"eslint-plugin-ghost": "3.4.0",
mocha: "10.2.0",
rollup: "2.79.1",
"rollup-plugin-babel": "4.4.0",
"rollup-plugin-commonjs": "10.1.0",
"rollup-plugin-node-resolve": "5.2.0",
"rollup-plugin-polyfill-node": "0.12.0",
"rollup-plugin-replace": "2.2.0",
"rollup-plugin-terser": "7.0.2",
should: "13.2.3",
sinon: "17.0.1"
};
var dependencies = {
axios: "^1.0.0"
};
var gitHead = "4839d3f97de2120d98fa47677eed7591dfa20e64";
var packageInfo = {
name: name$1,
version: version,
repository: repository,
author: author,
license: license,
main: main,
"umd:main": "umd/content-api.min.js",
unpkg: unpkg,
module: module$1,
source: source,
files: files,
scripts: scripts,
publishConfig: publishConfig,
devDependencies: devDependencies,
dependencies: dependencies,
gitHead: gitHead
};
// @NOTE: this value is dynamically replaced based on browser/node environment
const USER_AGENT_DEFAULT = true;
const packageVersion = packageInfo.version;
const defaultAcceptVersionHeader = 'v5.0';
const supportedVersions = ['v2', 'v3', 'v4', 'v5', 'canary'];
const name = '@tryghost/content-api';
/**
* This method can go away in favor of only sending 'Accept-Version` headers
* once the Ghost API removes a concept of version from it's URLS (with Ghost v5)
*
* @param {string} [version] version in `v{major}` format
* @returns {string}
*/
const resolveAPIPrefix = (version) => {
let prefix;
// NOTE: the "version.match(/^v5\.\d+/)" expression should be changed to "version.match(/^v\d+\.\d+/)" once Ghost v5 is out
if (version === 'v5' || version === undefined || version.match(/^v5\.\d+/)) {
prefix = `/content/`;
} else if (version.match(/^v\d+\.\d+/)) {
const versionPrefix = /^(v\d+)\.\d+/.exec(version)[1];
prefix = `/${versionPrefix}/content/`;
} else {
prefix = `/${version}/content/`;
}
return prefix;
};
const defaultMakeRequest = ({url, method, params, headers}) => {
return axios[method](url, {
params,
paramsSerializer: (parameters) => {
return Object.keys(parameters).reduce((parts, k) => {
const val = encodeURIComponent([].concat(parameters[k]).join(','));
return parts.concat(`${k}=${val}`);
}, []).join('&');
},
headers
});
};
/**
*
* @param {Object} options
* @param {String} options.url
* @param {String} options.key
* @param {String} [options.ghostPath]
* @param {String|Boolean} options.version - a version string like v3, v4, v5 or boolean value identifying presence of Accept-Version header
* @param {String|Boolean} [options.userAgent] - value controlling the 'User-Agent' header should be sent with a request
* @param {Function} [options.makeRequest]
* @param {String} [options.host] Deprecated
*/
function GhostContentAPI({url, key, host, version, userAgent, ghostPath = 'ghost', makeRequest = defaultMakeRequest}) {
/**
* host parameter is deprecated
* @deprecated use "url" instead
*/
if (host) {
// eslint-disable-next-line
console.warn(`${name}: The 'host' parameter is deprecated, please use 'url' instead`);
if (!url) {
url = host;
}
}
if (this instanceof GhostContentAPI) {
return GhostContentAPI({url, key, version, userAgent, ghostPath, makeRequest});
}
if (version === undefined) {
throw new Error(`${name} Config Missing: 'version' is required. E.g. ${supportedVersions.join(',')}`);
}
let acceptVersionHeader;
if (typeof version === 'boolean') {
if (version === true) {
acceptVersionHeader = defaultAcceptVersionHeader;
}
version = undefined;
} else if (version && !supportedVersions.includes(version) && !(version.match(/^v\d+\.\d+/))) {
throw new Error(`${name} Config Invalid: 'version' ${version} is not supported`);
} else {
if (version === 'canary') {
// eslint-disable-next-line
console.warn(`${name}: The 'version' parameter has a deprecated format 'canary', please use 'v{major}.{minor}' format instead`);
acceptVersionHeader = defaultAcceptVersionHeader;
} else if (version.match(/^v\d+$/)) {
// eslint-disable-next-line
console.warn(`${name}: The 'version' parameter has a deprecated format 'v{major}', please use 'v{major}.{minor}' format instead`);
acceptVersionHeader = `${version}.0`;
} else {
acceptVersionHeader = version;
}
}
if (!url) {
throw new Error(`${name} Config Missing: 'url' is required. E.g. 'https://site.com'`);
}
if (!/https?:\/\//.test(url)) {
throw new Error(`${name} Config Invalid: 'url' ${url} requires a protocol. E.g. 'https://site.com'`);
}
if (url.endsWith('/')) {
throw new Error(`${name} Config Invalid: 'url' ${url} must not have a trailing slash. E.g. 'https://site.com'`);
}
if (ghostPath.endsWith('/') || ghostPath.startsWith('/')) {
throw new Error(`${name} Config Invalid: 'ghostPath' ${ghostPath} must not have a leading or trailing slash. E.g. 'ghost'`);
}
if (key && !/[0-9a-f]{26}/.test(key)) {
throw new Error(`${name} Config Invalid: 'key' ${key} must have 26 hex characters`);
}
if (userAgent === undefined) {
userAgent = USER_AGENT_DEFAULT;
}
const api = ['posts', 'authors', 'tags', 'pages', 'settings', 'tiers', 'newsletters', 'offers'].reduce((apiObject, resourceType) => {
function browse(options = {}, memberToken) {
return makeApiRequest(resourceType, options, null, memberToken);
}
function read(data, options = {}, memberToken) {
if (!data || !data.id && !data.slug) {
return Promise.reject(new Error(`${name} read requires an id or slug.`));
}
const params = Object.assign({}, data, options);
return makeApiRequest(resourceType, params, data.id || `slug/${data.slug}`, memberToken);
}
return Object.assign(apiObject, {
[resourceType]: {
read,
browse
}
});
}, {});
// Settings, tiers & newsletters only have browse methods, offers only has read
delete api.settings.read;
delete api.tiers.read;
delete api.newsletters.read;
delete api.offers.browse;
return api;
function makeApiRequest(resourceType, params, id, membersToken = null) {
if (!membersToken && !key) {
return Promise.reject(
new Error(`${name} Config Missing: 'key' is required.`)
);
}
delete params.id;
const headers = membersToken ? {
Authorization: `GhostMembers ${membersToken}`
} : {};
if (userAgent) {
if (typeof userAgent === 'boolean') {
headers['User-Agent'] = `GhostContentSDK/${packageVersion}`;
} else {
headers['User-Agent'] = userAgent;
}
}
if (acceptVersionHeader) {
headers['Accept-Version'] = acceptVersionHeader;
}
params = Object.assign({key}, params);
const apiUrl = `${url}/${ghostPath}/api${resolveAPIPrefix(version)}${resourceType}/${id ? id + '/' : ''}`;
return makeRequest({
url: apiUrl,
method: 'get',
params,
headers
})
.then((res) => {
if (!Array.isArray(res.data[resourceType])) {
return res.data[resourceType];
}
if (res.data[resourceType].length === 1 && !res.data.meta) {
return res.data[resourceType][0];
}
return Object.assign(res.data[resourceType], {meta: res.data.meta});
}).catch((err) => {
if (err.response && err.response.data && err.response.data.errors) {
const props = err.response.data.errors[0];
const toThrow = new Error(props.message);
const keys = Object.keys(props);
toThrow.name = props.type;
keys.forEach((k) => {
toThrow[k] = props[k];
});
toThrow.response = err.response;
// @TODO: remove in 2.0. We have enhanced the error handling, but we don't want to break existing implementations.
toThrow.request = err.request;
toThrow.config = err.config;
throw toThrow;
} else {
throw err;
}
});
}
}
export default GhostContentAPI;

File diff suppressed because it is too large Load Diff