massive lint

This commit is contained in:
Adam Matthiesen 2024-03-03 08:49:15 -08:00
parent 3192714355
commit 5f7ac8e4a6
54 changed files with 1005 additions and 797 deletions

View File

@ -7,5 +7,5 @@
"access": "public", "access": "public",
"baseBranch": "main", "baseBranch": "main",
"updateInternalDependencies": "patch", "updateInternalDependencies": "patch",
"ignore": ["playground","starlight-playground"] "ignore": ["playground", "starlight-playground"]
} }

View File

@ -5,7 +5,11 @@ import { TSGhostContentAPI } from "@ts-ghost/content-api";
describe("content-api", () => { describe("content-api", () => {
let api: TSGhostContentAPI; let api: TSGhostContentAPI;
beforeEach(() => { beforeEach(() => {
api = new TSGhostContentAPI("https://ghost.org", "59d4bf56c73c04a18c867dc3ba", "v5.0"); api = new TSGhostContentAPI(
"https://ghost.org",
"59d4bf56c73c04a18c867dc3ba",
"v5.0",
);
}); });
test("content-api", () => { test("content-api", () => {
@ -14,7 +18,11 @@ describe("content-api", () => {
test("content-api shouldn't instantiate with an incorrect url", () => { test("content-api shouldn't instantiate with an incorrect url", () => {
assert.throws(() => { assert.throws(() => {
const api = new TSGhostContentAPI("ghost.org", "59d4bf56c73c04a18c867dc3ba", "v5.0"); const api = new TSGhostContentAPI(
"ghost.org",
"59d4bf56c73c04a18c867dc3ba",
"v5.0",
);
api.settings; api.settings;
}); });
}); });

View File

@ -4,10 +4,7 @@ import type { Page, Post } from "../schemas/api";
// LOAD ENVIRONMENT VARIABLES // LOAD ENVIRONMENT VARIABLES
import { loadEnv } from "vite"; import { loadEnv } from "vite";
const { const { CONTENT_API_KEY, CONTENT_API_URL } = loadEnv(
CONTENT_API_KEY,
CONTENT_API_URL
} = loadEnv(
"all", "all",
process.cwd(), process.cwd(),
"CONTENT_", "CONTENT_",
@ -125,4 +122,4 @@ export const getFeaturedPosts = async () => {
posts: results.data, posts: results.data,
meta: results.meta, meta: results.meta,
}; };
}; };

View File

@ -1,2 +1,2 @@
export * from "./ghostAPI"; export * from "./ghostAPI";
export * from "./invariant"; export * from "./invariant";

View File

@ -44,4 +44,4 @@ export function invariant(
const value: string = provided ? `${prefix}: ${provided}` : prefix; const value: string = provided ? `${prefix}: ${provided}` : prefix;
throw new Error(value); throw new Error(value);
} }

View File

@ -1,22 +1,22 @@
import { createResolver, defineIntegration } from "astro-integration-kit";
import { corePlugins } from "astro-integration-kit/plugins";
import { GhostUserConfigSchema } from "./schemas/userconfig";
import { loadEnv } from "vite";
import { AstroError } from "astro/errors";
import c from "picocolors";
import path from "node:path"; import path from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { createResolver, defineIntegration } from "astro-integration-kit";
import { corePlugins } from "astro-integration-kit/plugins";
import { AstroError } from "astro/errors";
import fse from "fs-extra"; import fse from "fs-extra";
import c from "picocolors";
import { loadEnv } from "vite";
import { GhostUserConfigSchema } from "./schemas/userconfig";
import latestVersion from "./utils/latestVersion"; import latestVersion from "./utils/latestVersion";
// Import External Integrations // Import External Integrations
import sitemap from "@astrojs/sitemap"; import sitemap from "@astrojs/sitemap";
import robotsTxt from "astro-robots-txt"; import robotsTxt from "astro-robots-txt";
import ghostRSS from "./integrations/rssfeed";
// Import Internal Integrations // Import Internal Integrations
import ghostOGImages from "./integrations/satoriog" import ghostOGImages from "./integrations/satoriog";
import ghostRSS from "./integrations/rssfeed" import ghostThemeProvider from "./integrations/themeprovider";
import ghostThemeProvider from "./integrations/themeprovider"
// Load environment variables // Load environment variables
const ENV = loadEnv("all", process.cwd(), "CONTENT_API"); const ENV = loadEnv("all", process.cwd(), "CONTENT_API");
@ -24,166 +24,283 @@ const ENV = loadEnv("all", process.cwd(), "CONTENT_API");
/** Astro-GhostCMS Integration /** Astro-GhostCMS Integration
* @description This integration allows you to use GhostCMS as a headless CMS for your Astro project * @description This integration allows you to use GhostCMS as a headless CMS for your Astro project
* @see https://astro-ghostcms.xyz for the most up-to-date documentation! * @see https://astro-ghostcms.xyz for the most up-to-date documentation!
*/ */
export default defineIntegration({ export default defineIntegration({
name: "@matthiesenxyz/astro-ghostcms", name: "@matthiesenxyz/astro-ghostcms",
optionsSchema: GhostUserConfigSchema, optionsSchema: GhostUserConfigSchema,
plugins: [...corePlugins], plugins: [...corePlugins],
setup({ options, name }) { setup({ options, name }) {
const { resolve } = createResolver(import.meta.url); const { resolve } = createResolver(import.meta.url);
return { return {
"astro:config:setup": ({ "astro:config:setup": ({
watchIntegration, watchIntegration,
hasIntegration, hasIntegration,
addIntegration, addIntegration,
addVirtualImports, addVirtualImports,
addDts, addDts,
injectRoute, injectRoute,
logger logger,
}) => { }) => {
const GhostLogger = logger.fork(c.bold(c.blue('👻 Astro-GhostCMS'))); const GhostLogger = logger.fork(c.bold(c.blue("👻 Astro-GhostCMS")));
const GhostENVLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('ENV Check')}`); const GhostENVLogger = logger.fork(
const GhostIntegrationLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('Integrations')}`); `${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
const GhostRouteLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('Router')}`); "ENV Check",
)}`,
);
const GhostIntegrationLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
"Integrations",
)}`,
);
const GhostRouteLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
"Router",
)}`,
);
watchIntegration(resolve()) watchIntegration(resolve());
GhostLogger.info('Initializing @matthiesenxyz/astro-ghostcms...') GhostLogger.info("Initializing @matthiesenxyz/astro-ghostcms...");
const verbose = options.fullConsoleLogs; const verbose = options.fullConsoleLogs;
// Check for GhostCMS environment variables // Check for GhostCMS environment variables
GhostENVLogger.info(c.bold(c.yellow('Checking for GhostCMS environment variables & user configuration'))) GhostENVLogger.info(
if (ENV.CONTENT_API_KEY === undefined) { c.bold(
GhostENVLogger.error(c.bgRed(c.bold(c.white('CONTENT_API_KEY is not set in environment variables')))) c.yellow(
throw new AstroError(`${name} CONTENT_API_KEY is not set in environment variables`) "Checking for GhostCMS environment variables & user configuration",
} ),
),
);
if (ENV.CONTENT_API_KEY === undefined) {
GhostENVLogger.error(
c.bgRed(
c.bold(
c.white("CONTENT_API_KEY is not set in environment variables"),
),
),
);
throw new AstroError(
`${name} CONTENT_API_KEY is not set in environment variables`,
);
}
if (options.ghostURL === undefined) { if (options.ghostURL === undefined) {
GhostENVLogger.warn(c.bgYellow(c.bold(c.black('ghostURL is not set in user configuration falling back to environment variable')))) GhostENVLogger.warn(
if (ENV.CONTENT_API_URL === undefined) { c.bgYellow(
GhostENVLogger.error(c.bgRed(c.bold(c.white('CONTENT_API_URL is not set in environment variables')))) c.bold(
throw new AstroError(`${name} CONTENT_API_URL is not set in environment variables`) c.black(
} "ghostURL is not set in user configuration falling back to environment variable",
} ),
GhostENVLogger.info(c.bold(c.green('GhostCMS environment variables are set'))) ),
),
);
if (ENV.CONTENT_API_URL === undefined) {
GhostENVLogger.error(
c.bgRed(
c.bold(
c.white(
"CONTENT_API_URL is not set in environment variables",
),
),
),
);
throw new AstroError(
`${name} CONTENT_API_URL is not set in environment variables`,
);
}
}
GhostENVLogger.info(
c.bold(c.green("GhostCMS environment variables are set")),
);
// Set up Astro-GhostCMS Integrations // Set up Astro-GhostCMS Integrations
GhostIntegrationLogger.info(c.bold(c.magenta('Configuring Enabled Integrations'))) GhostIntegrationLogger.info(
c.bold(c.magenta("Configuring Enabled Integrations")),
);
// Theme Provider // Theme Provider
if ( !options.disableThemeProvider ) { if (!options.disableThemeProvider) {
addIntegration( addIntegration(
ghostThemeProvider({ ghostThemeProvider({
theme: options.ThemeProvider?.theme, theme: options.ThemeProvider?.theme,
verbose verbose,
}) }),
) );
} else { } else {
if (verbose) { if (verbose) {
GhostIntegrationLogger.info(c.gray('Theme Provider is disabled')) GhostIntegrationLogger.info(c.gray("Theme Provider is disabled"));
} }
} }
// Satori OG Images // Satori OG Images
if ( options.enableOGImages ) { if (options.enableOGImages) {
addIntegration(ghostOGImages({ verbose })) addIntegration(ghostOGImages({ verbose }));
} else { } else {
if (verbose) { if (verbose) {
GhostIntegrationLogger.info(c.gray('OG Image Provider is disabled')) GhostIntegrationLogger.info(
} c.gray("OG Image Provider is disabled"),
} );
}
}
// RSS Feed // RSS Feed
if ( options.enableRSSFeed ) { if (options.enableRSSFeed) {
addIntegration(ghostRSS({ verbose })) addIntegration(ghostRSS({ verbose }));
} else { } else {
if (verbose) { if (verbose) {
GhostIntegrationLogger.info(c.gray('RSS Feed is disabled')) GhostIntegrationLogger.info(c.gray("RSS Feed is disabled"));
} }
} }
// @ASTROJS/SITEMAP // @ASTROJS/SITEMAP
if ( !hasIntegration("@astrojs/sitemap") ) { if (!hasIntegration("@astrojs/sitemap")) {
if (verbose) { if (verbose) {
GhostIntegrationLogger.info(c.bold(c.magenta(`Adding ${c.blue("@astrojs/sitemap")} integration`))) GhostIntegrationLogger.info(
} c.bold(
addIntegration(sitemap()) c.magenta(`Adding ${c.blue("@astrojs/sitemap")} integration`),
} else { ),
if (verbose) { );
GhostIntegrationLogger.info(c.gray('@astrojs/sitemap integration already exists, skipping...')) }
} addIntegration(sitemap());
} } else {
// ASTRO-ROBOTS-TXT if (verbose) {
if ( !hasIntegration("@astro-robots-txt") ) { GhostIntegrationLogger.info(
if (verbose) { c.gray(
GhostIntegrationLogger.info(c.bold(c.magenta(`Adding ${c.blue("astro-robots-txt")} integration`))) "@astrojs/sitemap integration already exists, skipping...",
} ),
addIntegration(robotsTxt()) );
} else { }
if (verbose) { }
GhostIntegrationLogger.info(c.gray('astro-robots-txt integration already exists, skipping...')) // ASTRO-ROBOTS-TXT
} if (!hasIntegration("@astro-robots-txt")) {
} if (verbose) {
GhostIntegrationLogger.info(
c.bold(
c.magenta(`Adding ${c.blue("astro-robots-txt")} integration`),
),
);
}
addIntegration(robotsTxt());
} else {
if (verbose) {
GhostIntegrationLogger.info(
c.gray(
"astro-robots-txt integration already exists, skipping...",
),
);
}
}
// Set up default 404 page // Set up default 404 page
if ( !options.disableDefault404 ) { if (!options.disableDefault404) {
if (verbose) { if (verbose) {
GhostRouteLogger.info(c.bold(c.cyan('Setting up default 404 page'))) GhostRouteLogger.info(
} c.bold(c.cyan("Setting up default 404 page")),
injectRoute({ );
pattern: "/404", }
entrypoint: `${name}/404.astro`, injectRoute({
prerender: true pattern: "/404",
}) entrypoint: `${name}/404.astro`,
} else { prerender: true,
if (verbose) { });
GhostRouteLogger.info(c.gray('Default 404 page is disabled, Skipping...')) } else {
} if (verbose) {
} GhostRouteLogger.info(
c.gray("Default 404 page is disabled, Skipping..."),
);
}
}
// Add virtual imports for user configuration // Add virtual imports for user configuration
addVirtualImports({ addVirtualImports({
'virtual:@matthiesenxyz/astro-ghostcms/config': `export default ${JSON.stringify(options)}`, "virtual:@matthiesenxyz/astro-ghostcms/config": `export default ${JSON.stringify(
}) options,
)}`,
});
// Add types for user configuration // Add types for user configuration
addDts({ addDts({
name: "@matthiesenxyz/astro-ghostcms/config", name: "@matthiesenxyz/astro-ghostcms/config",
content: `declare module "virtual:@matthiesenxyz/astro-ghostcms/config" { content: `declare module "virtual:@matthiesenxyz/astro-ghostcms/config" {
const Config: import("../schemas/userconfig").GhostUserConfig; const Config: import("../schemas/userconfig").GhostUserConfig;
export default Config; export default Config;
}` }`,
}) });
},
"astro:config:done": ({ logger }) => {
const GhostLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.green(
"CONFIG",
)}`,
);
GhostLogger.info(
c.bold(c.green("Integration Setup & Configuration Complete")),
);
},
"astro:server:start": async ({ logger }) => {
const GhostLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.bold(
c.green("DEV"),
)}`,
);
const GhostUpdateLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.bold(
c.green("VERSION CHECK"),
)}`,
);
}, // Start the DEV server
"astro:config:done": ({ logger }) => { GhostLogger.info(
const GhostLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.green('CONFIG')}`); c.bold(c.magenta("Running Astro-GhostCMS in Deveopment mode 🚀")),
GhostLogger.info(c.bold(c.green('Integration Setup & Configuration Complete'))) );
},
"astro:server:start": async ({ logger }) => {
const GhostLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.bold(c.green('DEV'))}`);
const GhostUpdateLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.bold(c.green('VERSION CHECK'))}`);
// Start the DEV server // Check for updates
GhostLogger.info(c.bold(c.magenta('Running Astro-GhostCMS in Deveopment mode 🚀'))) const currentNPMVersion = await latestVersion(
"@matthiesenxyz/astro-ghostcms",
);
const packageJson = await fse.readJson(
path.resolve(fileURLToPath(import.meta.url), "../../package.json"),
);
const localVersion = packageJson.version;
// Check for updates if (currentNPMVersion !== localVersion) {
const currentNPMVersion = await latestVersion("@matthiesenxyz/astro-ghostcms"); GhostUpdateLogger.warn(
const packageJson = await fse.readJson(path.resolve(fileURLToPath(import.meta.url), "../../package.json")); `\n${c.bgYellow(
const localVersion = packageJson.version; c.bold(
c.black(
if (currentNPMVersion !== localVersion) { " There is a new version of Astro-GhostCMS available! ",
GhostUpdateLogger.warn(`\n${c.bgYellow(c.bold(c.black(" There is a new version of Astro-GhostCMS available! ")))}\n${c.bold(c.white(" Current Installed Version: ")) + c.bold(c.red(`${localVersion} `))} \n ${c.bold(c.white("New Available Version: "))} ${c.green(currentNPMVersion)} \n ${c.bold(c.white("Please consider updating to the latest version by running: "))} ${c.bold(c.green("npm i @matthiesenxyz/astro-ghostcms@latest"))} \n`) ),
} else { ),
GhostUpdateLogger.info(c.bold(c.green(`Astro-GhostCMS is up to date! v${localVersion}`))) )}\n${
} c.bold(c.white(" Current Installed Version: ")) +
c.bold(c.red(`${localVersion} `))
}, } \n ${c.bold(c.white("New Available Version: "))} ${c.green(
"astro:build:done": ({ logger }) => { currentNPMVersion,
const GhostLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.bold(c.green('BUILD'))}`); )} \n ${c.bold(
GhostLogger.info(c.bold(c.magenta('Running Astro-GhostCMS in Production mode 🚀'))) c.white(
} "Please consider updating to the latest version by running: ",
} ),
} )} ${c.bold(
}) c.green("npm i @matthiesenxyz/astro-ghostcms@latest"),
)} \n`,
);
} else {
GhostUpdateLogger.info(
c.bold(c.green(`Astro-GhostCMS is up to date! v${localVersion}`)),
);
}
},
"astro:build:done": ({ logger }) => {
const GhostLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.bold(
c.green("BUILD"),
)}`,
);
GhostLogger.info(
c.bold(c.magenta("Running Astro-GhostCMS in Production mode 🚀")),
);
},
};
},
});

View File

@ -1,3 +1,3 @@
import astroghostcms from './astro-ghostcms.js'; import astroghostcms from "./astro-ghostcms.js";
export default astroghostcms; export default astroghostcms;

View File

@ -1,29 +1,29 @@
import { createResolver, defineIntegration } from "astro-integration-kit"; import { createResolver, defineIntegration } from "astro-integration-kit";
import { corePlugins } from "astro-integration-kit/plugins"; import { corePlugins } from "astro-integration-kit/plugins";
import c from "picocolors";
import { z } from "astro/zod"; import { z } from "astro/zod";
import c from "picocolors";
export default defineIntegration({ export default defineIntegration({
name: "@matthiesenxyz/astro-ghostcms-rss", name: "@matthiesenxyz/astro-ghostcms-rss",
optionsSchema: z.object({ optionsSchema: z.object({
verbose: z.boolean().optional().default(false), verbose: z.boolean().optional().default(false),
}), }),
plugins: [...corePlugins], plugins: [...corePlugins],
setup({ options }) { setup({ options }) {
const { resolve } = createResolver(import.meta.url); const { resolve } = createResolver(import.meta.url);
return { return {
"astro:config:setup": ({ "astro:config:setup": ({ watchIntegration, injectRoute, logger }) => {
watchIntegration, watchIntegration(resolve());
injectRoute, const RSSLogger = logger.fork(
logger `${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
}) => { "RSSGenerator",
watchIntegration(resolve()) )}`,
const RSSLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('RSSGenerator')}`); );
RSSLogger.info(c.bold(c.magenta('RSS Feed Enabled. Setting up...'))) RSSLogger.info(c.bold(c.magenta("RSS Feed Enabled. Setting up...")));
const rssRoute = "@matthiesenxyz/astro-ghostcms/rss-routes" const rssRoute = "@matthiesenxyz/astro-ghostcms/rss-routes";
injectRoute({ injectRoute({
pattern: "/rss-style.xsl", pattern: "/rss-style.xsl",
@ -34,15 +34,18 @@ export default defineIntegration({
pattern: "/rss.xml", pattern: "/rss.xml",
entrypoint: `${rssRoute}/rss.xml.ts`, entrypoint: `${rssRoute}/rss.xml.ts`,
}); });
},
"astro:config:done": ({ logger }) => {
const RSSLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
"RSSGenerator",
)}`,
);
}, if (options.verbose) {
"astro:config:done": ({ logger }) => { RSSLogger.info(c.bold(c.green("RSS Feed Setup Complete")));
const RSSLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('RSSGenerator')}`); }
},
if (options.verbose) { };
RSSLogger.info(c.bold(c.green('RSS Feed Setup Complete'))) },
} });
}
}
}
})

View File

@ -78,4 +78,4 @@ export async function GET() {
}, },
}, },
); );
} }

View File

@ -24,4 +24,4 @@ export async function GET({ site }: APIContext) {
author: post.primary_author?.name, author: post.primary_author?.name,
})), })),
}); });
} }

View File

@ -1,31 +1,37 @@
import { createResolver, defineIntegration } from "astro-integration-kit"; import { createResolver, defineIntegration } from "astro-integration-kit";
import { corePlugins } from "astro-integration-kit/plugins"; import { corePlugins } from "astro-integration-kit/plugins";
import c from "picocolors";
import { z } from "astro/zod"; import { z } from "astro/zod";
import c from "picocolors";
export default defineIntegration({ export default defineIntegration({
name: "@matthiesenxyz/astro-ghostcms-satoriog", name: "@matthiesenxyz/astro-ghostcms-satoriog",
optionsSchema: z.object({ optionsSchema: z.object({
verbose: z.boolean().optional().default(false), verbose: z.boolean().optional().default(false),
}), }),
plugins: [...corePlugins], plugins: [...corePlugins],
setup({ options }) { setup({ options }) {
const { resolve } = createResolver(import.meta.url); const { resolve } = createResolver(import.meta.url);
return { return {
"astro:config:setup": ({ "astro:config:setup": ({
watchIntegration, watchIntegration,
updateConfig, updateConfig,
injectRoute, injectRoute,
logger logger,
}) => { }) => {
watchIntegration(resolve()) watchIntegration(resolve());
const SatoriLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('SatoriOG')}`); const SatoriLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
"SatoriOG",
)}`,
);
SatoriLogger.info(c.bold(c.magenta('OG Image Integration Enabled. Setting up...'))) SatoriLogger.info(
c.bold(c.magenta("OG Image Integration Enabled. Setting up...")),
);
const pkgname = "@matthiesenxyz/astro-ghostcms/open-graph" const pkgname = "@matthiesenxyz/astro-ghostcms/open-graph";
injectRoute({ injectRoute({
pattern: "/open-graph/[slug].png", pattern: "/open-graph/[slug].png",
@ -57,17 +63,23 @@ export default defineIntegration({
entrypoint: `${pkgname}/tag/[slug].png.ts`, entrypoint: `${pkgname}/tag/[slug].png.ts`,
}); });
updateConfig({ updateConfig({
vite: { optimizeDeps: { exclude: ["@resvg/resvg-js"] } } vite: { optimizeDeps: { exclude: ["@resvg/resvg-js"] } },
}) });
}, },
"astro:config:done": ({ logger }) => { "astro:config:done": ({ logger }) => {
const SatoriLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('SatoriOG')}`); const SatoriLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
"SatoriOG",
)}`,
);
if (options.verbose) { if (options.verbose) {
SatoriLogger.info(c.bold(c.green('OG Image Integration Setup Complete'))) SatoriLogger.info(
c.bold(c.green("OG Image Integration Setup Complete")),
);
} }
} },
} };
} },
}) });

View File

@ -5,12 +5,7 @@ import type {
InferGetStaticPropsType, InferGetStaticPropsType,
} from "astro"; } from "astro";
import { html } from "satori-html"; import { html } from "satori-html";
import { import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api";
getAllPages,
getAllPosts,
getSettings,
invariant,
} from "../../../api";
import satoriOG from "../satori"; import satoriOG from "../satori";
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => {
@ -67,4 +62,4 @@ export const GET: APIRoute = async ({ props, site }) => {
], ],
}, },
}); });
}; };

View File

@ -61,4 +61,4 @@ export const GET: APIRoute = async ({ props, site }) => {
], ],
}, },
}); });
}; };

View File

@ -5,12 +5,7 @@ import type {
InferGetStaticPropsType, InferGetStaticPropsType,
} from "astro"; } from "astro";
import { html } from "satori-html"; import { html } from "satori-html";
import { import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api";
getAllPages,
getAllPosts,
getSettings,
invariant,
} from "../../../api";
import satoriOG from "../satori"; import satoriOG from "../satori";
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => {
@ -65,4 +60,4 @@ export const GET: APIRoute = async ({ props, site }) => {
], ],
}, },
}); });
}; };

View File

@ -5,12 +5,7 @@ import type {
InferGetStaticPropsType, InferGetStaticPropsType,
} from "astro"; } from "astro";
import { html } from "satori-html"; import { html } from "satori-html";
import { import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api";
getAllPages,
getAllPosts,
getSettings,
invariant,
} from "../../../api";
import satoriOG from "../satori"; import satoriOG from "../satori";
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => {
@ -65,4 +60,4 @@ export const GET: APIRoute = async ({ props, site }) => {
], ],
}, },
}); });
}; };

View File

@ -68,4 +68,4 @@ export const GET: APIRoute = async ({ props, site }) => {
], ],
}, },
}); });
}; };

View File

@ -5,12 +5,7 @@ import type {
InferGetStaticPropsType, InferGetStaticPropsType,
} from "astro"; } from "astro";
import { html } from "satori-html"; import { html } from "satori-html";
import { import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api";
getAllPages,
getAllPosts,
getSettings,
invariant,
} from "../../../api";
import satoriOG from "../satori"; import satoriOG from "../satori";
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => {
@ -65,4 +60,4 @@ export const GET: APIRoute = async ({ props, site }) => {
], ],
}, },
}); });
}; };

View File

@ -52,4 +52,4 @@ export function getOgImagePath(inputFilename = "index"): string {
filename = "index"; filename = "index";
} }
return `./open-graph/${filename}.png`; return `./open-graph/${filename}.png`;
} }

View File

@ -17,4 +17,4 @@ export type ToImageOptions = {
| ResvgOptions | ResvgOptions
| ((params: { width: number; height: number }) => ResvgOptions); | ((params: { width: number; height: number }) => ResvgOptions);
}; };
export type ToResponseOptions = ToImageOptions & { response?: ResponseInit }; export type ToResponseOptions = ToImageOptions & { response?: ResponseInit };

View File

@ -4,32 +4,43 @@ import { z } from "astro/zod";
import c from "picocolors"; import c from "picocolors";
export default defineIntegration({ export default defineIntegration({
name: "@matthiesenxyz/astro-ghostcms-themeprovider", name: "@matthiesenxyz/astro-ghostcms-themeprovider",
optionsSchema: z.object({ optionsSchema: z.object({
theme: z.string().optional().default("@matthiesenxyz/astro-ghostcms-theme-default"), theme: z
.string()
.optional()
.default("@matthiesenxyz/astro-ghostcms-theme-default"),
verbose: z.boolean().optional().default(false), verbose: z.boolean().optional().default(false),
}), }),
plugins: [...corePlugins], plugins: [...corePlugins],
setup({ options }) { setup({ options }) {
const { resolve } = createResolver(import.meta.url); const { resolve } = createResolver(import.meta.url);
return { return {
"astro:config:setup": ({ "astro:config:setup": ({ watchIntegration, injectRoute, logger }) => {
watchIntegration, watchIntegration(resolve());
injectRoute,
logger
}) => {
watchIntegration(resolve())
const themeLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('Theme Provider')}`); const themeLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
"Theme Provider",
)}`,
);
themeLogger.info(c.bold(c.magenta('Theme Provider enabled. Setting up...'))) themeLogger.info(
c.bold(c.magenta("Theme Provider enabled. Setting up...")),
);
if ( options.verbose) { if (options.verbose) {
if ( options.theme === "@matthiesenxyz/astro-ghostcms-theme-default" ) { if (options.theme === "@matthiesenxyz/astro-ghostcms-theme-default") {
themeLogger.info(c.blue('No theme is set, injecting default theme')) themeLogger.info(
c.blue("No theme is set, injecting default theme"),
);
} else { } else {
themeLogger.info(`${c.bold(c.cyan("Injecting Theme:"))} ${c.bold(c.underline(c.magenta(options.theme)))}`) themeLogger.info(
`${c.bold(c.cyan("Injecting Theme:"))} ${c.bold(
c.underline(c.magenta(options.theme)),
)}`,
);
} }
} }
@ -38,7 +49,7 @@ export default defineIntegration({
entrypoint: `${options.theme}/index.astro`, entrypoint: `${options.theme}/index.astro`,
}); });
injectRoute({ injectRoute({
pattern: "/[slug]", pattern: "/[slug]",
entrypoint: `${options.theme}/[slug].astro`, entrypoint: `${options.theme}/[slug].astro`,
}); });
@ -66,16 +77,19 @@ export default defineIntegration({
injectRoute({ injectRoute({
pattern: "/archives/[...page]", pattern: "/archives/[...page]",
entrypoint: `${options.theme}/archives/[...page].astro`, entrypoint: `${options.theme}/archives/[...page].astro`,
}); });
},
"astro:config:done": ({ logger }) => {
const themeLogger = logger.fork(
`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(
"Theme Provider",
)}`,
);
}, if (options.verbose) {
"astro:config:done": ({ logger }) => { themeLogger.info(c.bold(c.green("Provider Setup Complete")));
const themeLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('Theme Provider')}`);
if ( options.verbose ) {
themeLogger.info(c.bold(c.green('Provider Setup Complete')))
} }
} },
} };
} },
}) });

View File

@ -1,146 +1,164 @@
import createFetchMock from "vitest-fetch-mock";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import createFetchMock from "vitest-fetch-mock";
import { TSGhostContentAPI } from "@ts-ghost/content-api"; import { TSGhostContentAPI } from "@ts-ghost/content-api";
const fetchMocker = createFetchMock(vi); const fetchMocker = createFetchMock(vi);
describe("authors api .browse() Args Type-safety", () => { describe("authors api .browse() Args Type-safety", () => {
const url = process.env.VITE_GHOST_URL || "https://my-ghost-blog.com"; const url = process.env.VITE_GHOST_URL || "https://my-ghost-blog.com";
const key = process.env.VITE_GHOST_CONTENT_API_KEY || "59d4bf56c73c04a18c867dc3ba"; const key =
const api = new TSGhostContentAPI(url, key, "v5.0"); process.env.VITE_GHOST_CONTENT_API_KEY || "59d4bf56c73c04a18c867dc3ba";
test(".browse() params shouldnt accept invalid params", () => { const api = new TSGhostContentAPI(url, key, "v5.0");
// @ts-expect-error - shouldnt accept invalid params test(".browse() params shouldnt accept invalid params", () => {
const browse = api.authors.browse({ pp: 2 }); // @ts-expect-error - shouldnt accept invalid params
expect(browse.getParams().browseParams).toStrictEqual({}); const browse = api.authors.browse({ pp: 2 });
}); expect(browse.getParams().browseParams).toStrictEqual({});
});
test(".browse() 'order' params should ony accept fields values", () => { test(".browse() 'order' params should ony accept fields values", () => {
// @ts-expect-error - order should ony contain field // @ts-expect-error - order should ony contain field
expect(() => api.authors.browse({ order: "foo ASC" })).toThrow(); expect(() => api.authors.browse({ order: "foo ASC" })).toThrow();
// valid // valid
expect(api.authors.browse({ order: "name ASC" }).getParams().browseParams).toStrictEqual({ expect(
order: "name ASC", api.authors.browse({ order: "name ASC" }).getParams().browseParams,
}); ).toStrictEqual({
expect(api.authors.browse({ order: "name ASC,slug DESC" }).getParams().browseParams).toStrictEqual({ order: "name ASC",
order: "name ASC,slug DESC", });
}); expect(
expect( api.authors.browse({ order: "name ASC,slug DESC" }).getParams()
api.authors.browse({ order: "name ASC,slug DESC,location ASC" }).getParams().browseParams .browseParams,
).toStrictEqual({ ).toStrictEqual({
order: "name ASC,slug DESC,location ASC", order: "name ASC,slug DESC",
}); });
// @ts-expect-error - order should ony contain field (There is a typo in location) expect(
expect(() => api.authors.browse({ order: "name ASC,slug DESC,locaton ASC" })).toThrow(); api.authors
}); .browse({ order: "name ASC,slug DESC,location ASC" })
.getParams().browseParams,
).toStrictEqual({
order: "name ASC,slug DESC,location ASC",
});
// @ts-expect-error - order should ony contain field (There is a typo in location)
expect(() =>
api.authors.browse({ order: "name ASC,slug DESC,locaton ASC" }),
).toThrow();
});
test(".browse() 'filter' params should ony accept valid field", () => { test(".browse() 'filter' params should ony accept valid field", () => {
expect(() => expect(() =>
api.authors.browse({ api.authors.browse({
// @ts-expect-error - order should ony contain field // @ts-expect-error - order should ony contain field
filter: "foo:bar", filter: "foo:bar",
}) }),
).toThrow(); ).toThrow();
expect( expect(
api.authors api.authors
.browse({ .browse({
filter: "name:bar", filter: "name:bar",
}) })
.getParams().browseParams .getParams().browseParams,
).toStrictEqual({ ).toStrictEqual({
filter: "name:bar", filter: "name:bar",
}); });
expect( expect(
api.authors api.authors
.browse({ .browse({
filter: "name:bar+slug:-test", filter: "name:bar+slug:-test",
}) })
.getParams().browseParams .getParams().browseParams,
).toStrictEqual({ ).toStrictEqual({
filter: "name:bar+slug:-test", filter: "name:bar+slug:-test",
}); });
}); });
test(".browse 'fields' argument should ony accept valid fields", () => { test(".browse 'fields' argument should ony accept valid fields", () => {
expect( expect(
api.authors api.authors
.browse() .browse()
.fields({ .fields({
// @ts-expect-error - order should ony contain field // @ts-expect-error - order should ony contain field
foo: true, foo: true,
}) })
.getOutputFields() .getOutputFields(),
).toEqual([]); ).toEqual([]);
expect(api.authors.browse().fields({ location: true }).getOutputFields()).toEqual(["location"]); expect(
expect(api.authors.browse().fields({ name: true, website: true }).getOutputFields()).toEqual([ api.authors.browse().fields({ location: true }).getOutputFields(),
"name", ).toEqual(["location"]);
"website", expect(
]); api.authors
}); .browse()
.fields({ name: true, website: true })
.getOutputFields(),
).toEqual(["name", "website"]);
});
}); });
describe("authors resource mocked", () => { describe("authors resource mocked", () => {
let api: TSGhostContentAPI; let api: TSGhostContentAPI;
beforeEach(() => { beforeEach(() => {
api = new TSGhostContentAPI("https://my-ghost-blog.com", "59d4bf56c73c04a18c867dc3ba", "v5.0"); api = new TSGhostContentAPI(
fetchMocker.enableMocks(); "https://my-ghost-blog.com",
}); "59d4bf56c73c04a18c867dc3ba",
afterEach(() => { "v5.0",
vi.restoreAllMocks(); );
}); fetchMocker.enableMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
test("aouthors should be fetched correctly", async () => { test("aouthors should be fetched correctly", async () => {
const authors = api.authors; const authors = api.authors;
expect(authors).not.toBeUndefined(); expect(authors).not.toBeUndefined();
const browseQuery = authors const browseQuery = authors
.browse({ .browse({
page: 2, page: 2,
}) })
.fields({ .fields({
name: true, name: true,
id: true, id: true,
}); });
expect(browseQuery).not.toBeUndefined(); expect(browseQuery).not.toBeUndefined();
expect(browseQuery.getOutputFields()).toStrictEqual(["name", "id"]); expect(browseQuery.getOutputFields()).toStrictEqual(["name", "id"]);
fetchMocker.doMockOnce( fetchMocker.doMockOnce(
JSON.stringify({ JSON.stringify({
authors: [ authors: [
{ {
name: "foo", name: "foo",
id: "eaoizdjoa1321123", id: "eaoizdjoa1321123",
}, },
], ],
meta: { meta: {
pagination: { pagination: {
page: 1, page: 1,
limit: 15, limit: 15,
pages: 1, pages: 1,
total: 1, total: 1,
next: null, next: null,
prev: null, prev: null,
}, },
}, },
}) }),
); );
const result = await browseQuery.fetch(); const result = await browseQuery.fetch();
expect(fetchMocker).toHaveBeenCalledTimes(1); expect(fetchMocker).toHaveBeenCalledTimes(1);
expect(fetchMocker).toHaveBeenCalledWith( expect(fetchMocker).toHaveBeenCalledWith(
"https://my-ghost-blog.com/ghost/api/content/authors/?page=2&fields=name%2Cid&key=59d4bf56c73c04a18c867dc3ba", "https://my-ghost-blog.com/ghost/api/content/authors/?page=2&fields=name%2Cid&key=59d4bf56c73c04a18c867dc3ba",
{ {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"Accept-Version": "v5.0", "Accept-Version": "v5.0",
}, },
} },
); );
expect(result).not.toBeUndefined(); expect(result).not.toBeUndefined();
if (result.success) { if (result.success) {
expect(result.data.length).toBe(1); expect(result.data.length).toBe(1);
expect(result.data[0].name).toBe("foo"); expect(result.data[0].name).toBe("foo");
expect(result.data[0].id).toBe("eaoizdjoa1321123"); expect(result.data[0].id).toBe("eaoizdjoa1321123");
} }
}); });
}); });

View File

@ -34,4 +34,4 @@ export const ghostFetchAuthorsSchema = z.object({
export const authorsIncludeSchema = z.object({ export const authorsIncludeSchema = z.object({
"count.posts": z.literal(true).optional(), "count.posts": z.literal(true).optional(),
}); });
export type AuthorsIncludeSchema = z.infer<typeof authorsIncludeSchema>; export type AuthorsIncludeSchema = z.infer<typeof authorsIncludeSchema>;

View File

@ -4,4 +4,4 @@ export * from "./posts";
export * from "./settings"; export * from "./settings";
export * from "./socials"; export * from "./socials";
export * from "./tags"; export * from "./tags";
export * from "./tiers"; export * from "./tiers";

View File

@ -52,4 +52,4 @@ export const pagesIncludeSchema = z.object({
authors: z.literal(true).optional(), authors: z.literal(true).optional(),
tags: z.literal(true).optional(), tags: z.literal(true).optional(),
}); });
export type PagesIncludeSchema = z.infer<typeof pagesIncludeSchema>; export type PagesIncludeSchema = z.infer<typeof pagesIncludeSchema>;

View File

@ -4,52 +4,64 @@ import { TSGhostContentAPI } from "@ts-ghost/content-api";
import type { Post } from "./index"; import type { Post } from "./index";
const url = process.env.VITE_GHOST_URL || "https://my-ghost-blog.com"; const url = process.env.VITE_GHOST_URL || "https://my-ghost-blog.com";
const key = process.env.VITE_GHOST_CONTENT_API_KEY || "59d4bf56c73c04a18c867dc3ba"; const key =
process.env.VITE_GHOST_CONTENT_API_KEY || "59d4bf56c73c04a18c867dc3ba";
describe("posts api .browse() Args Type-safety", () => { describe("posts api .browse() Args Type-safety", () => {
const api = new TSGhostContentAPI(url, key, "v5.0"); const api = new TSGhostContentAPI(url, key, "v5.0");
test(".browse() params shouldnt accept invalid params", () => { test(".browse() params shouldnt accept invalid params", () => {
// @ts-expect-error - shouldnt accept invalid params // @ts-expect-error - shouldnt accept invalid params
const browse = api.posts.browse({ pp: 2 }); const browse = api.posts.browse({ pp: 2 });
expect(browse.getParams().browseParams).toStrictEqual({}); expect(browse.getParams().browseParams).toStrictEqual({});
const outputFields = { const outputFields = {
slug: true, slug: true,
title: true, title: true,
// @ts-expect-error - shouldnt accept invalid params // @ts-expect-error - shouldnt accept invalid params
foo: true, foo: true,
} satisfies { [k in keyof Post]?: true | undefined }; } satisfies { [k in keyof Post]?: true | undefined };
const test = api.posts const test = api.posts
.browse() .browse()
// @ts-expect-error - shouldnt accept invalid params // @ts-expect-error - shouldnt accept invalid params
.fields(outputFields); .fields(outputFields);
expect(test.getOutputFields()).toEqual(["slug", "title"]); expect(test.getOutputFields()).toEqual(["slug", "title"]);
const fields = ["slug", "title", "foo"] as const; const fields = ["slug", "title", "foo"] as const;
const unknownOriginFields = fields.reduce((acc, k) => { const unknownOriginFields = fields.reduce(
acc[k as keyof Post] = true; (acc, k) => {
return acc; acc[k as keyof Post] = true;
}, {} as { [k in keyof Post]?: true | undefined }); return acc;
const result = api.posts.browse().fields(unknownOriginFields); },
expect(result.getOutputFields()).toEqual(["slug", "title"]); {} as { [k in keyof Post]?: true | undefined },
}); );
test(".browse() params, output fields declare const", () => { const result = api.posts.browse().fields(unknownOriginFields);
const outputFields = { expect(result.getOutputFields()).toEqual(["slug", "title"]);
slug: true, });
title: true, test(".browse() params, output fields declare const", () => {
} satisfies { [k in keyof Post]?: true | undefined }; const outputFields = {
slug: true,
title: true,
} satisfies { [k in keyof Post]?: true | undefined };
const test = api.posts.browse().fields(outputFields); const test = api.posts.browse().fields(outputFields);
expect(test.getOutputFields()).toEqual(["slug", "title"]); expect(test.getOutputFields()).toEqual(["slug", "title"]);
// @ts-expect-error - shouldnt accept invalid params // @ts-expect-error - shouldnt accept invalid params
expect(() => api.posts.browse({ filter: "slugg:test" })).toThrow(); expect(() => api.posts.browse({ filter: "slugg:test" })).toThrow();
// @ts-expect-error - shouldnt accept invalid params // @ts-expect-error - shouldnt accept invalid params
expect(() => api.posts.browse({ filter: "slug:test,foo:-[bar,baz]" })).toThrow(); expect(() =>
expect(api.posts.browse({ filter: "slug:test,tags:-[bar,baz]" })).toBeDefined(); api.posts.browse({ filter: "slug:test,foo:-[bar,baz]" }),
expect(api.posts.browse({ filter: "slug:test,tags:[bar,baz]" })).toBeDefined(); ).toThrow();
// @ts-expect-error - shouldnt accept invalid params expect(
expect(() => api.posts.browse({ filter: "slug:test,food:-[bar,baz]" })).toThrow(); api.posts.browse({ filter: "slug:test,tags:-[bar,baz]" }),
}); ).toBeDefined();
}); expect(
api.posts.browse({ filter: "slug:test,tags:[bar,baz]" }),
).toBeDefined();
// @ts-expect-error - shouldnt accept invalid params
expect(() =>
api.posts.browse({ filter: "slug:test,food:-[bar,baz]" }),
).toThrow();
});
});

View File

@ -51,4 +51,4 @@ export const postsIncludeSchema = z.object({
authors: z.literal(true).optional(), authors: z.literal(true).optional(),
tags: z.literal(true).optional(), tags: z.literal(true).optional(),
}); });
export type PostsIncludeSchema = z.infer<typeof postsIncludeSchema>; export type PostsIncludeSchema = z.infer<typeof postsIncludeSchema>;

View File

@ -3,34 +3,37 @@ import { beforeEach, describe, expect, test } from "vitest";
import { TSGhostContentAPI } from "@ts-ghost/content-api"; import { TSGhostContentAPI } from "@ts-ghost/content-api";
const url = process.env.VITE_GHOST_URL || "https://my-ghost-blog.com"; const url = process.env.VITE_GHOST_URL || "https://my-ghost-blog.com";
const key = process.env.VITE_GHOST_CONTENT_API_KEY || "59d4bf56c73c04a18c867dc3ba"; const key =
process.env.VITE_GHOST_CONTENT_API_KEY || "59d4bf56c73c04a18c867dc3ba";
describe("settings integration tests browse", () => { describe("settings integration tests browse", () => {
let api: TSGhostContentAPI; let api: TSGhostContentAPI;
beforeEach(() => { beforeEach(() => {
api = new TSGhostContentAPI(url, key, "v5.0"); api = new TSGhostContentAPI(url, key, "v5.0");
}); });
test("settings.fetch()", async () => { test("settings.fetch()", async () => {
const result = await api.settings.fetch(); const result = await api.settings.fetch();
expect(result).not.toBeUndefined(); expect(result).not.toBeUndefined();
expect(result).not.toBeNull(); expect(result).not.toBeNull();
if (!result.success) { if (!result.success) {
expect(result.errors).toBeDefined(); expect(result.errors).toBeDefined();
expect(result.errors).toHaveLength(1); expect(result.errors).toHaveLength(1);
} else { } else {
expect(result.data).toBeDefined(); expect(result.data).toBeDefined();
const settings = result.data; const settings = result.data;
expect(settings).toBeDefined(); expect(settings).toBeDefined();
expect(settings.title).toBe("Astro Starter"); expect(settings.title).toBe("Astro Starter");
expect(settings.description).toBe("Thoughts, stories and ideas."); expect(settings.description).toBe("Thoughts, stories and ideas.");
expect(settings.logo).toBeNull(); expect(settings.logo).toBeNull();
expect(settings.cover_image).toBe("https://static.ghost.org/v4.0.0/images/publication-cover.jpg"); expect(settings.cover_image).toBe(
expect(settings.icon).toBeNull(); "https://static.ghost.org/v4.0.0/images/publication-cover.jpg",
expect(settings.lang).toBe("en"); );
expect(settings.timezone).toBe("Etc/UTC"); expect(settings.icon).toBeNull();
expect(settings.codeinjection_head).toBeNull(); expect(settings.lang).toBe("en");
expect(settings.codeinjection_foot).toBeNull(); expect(settings.timezone).toBe("Etc/UTC");
expect(settings.members_support_address).toBe("noreply"); expect(settings.codeinjection_head).toBeNull();
} expect(settings.codeinjection_foot).toBeNull();
}); expect(settings.members_support_address).toBe("noreply");
}); }
});
});

View File

@ -37,4 +37,4 @@ export const settingsSchema = z.object({
url: z.string(), url: z.string(),
}); });
export type Settings = z.infer<typeof settingsSchema>; export type Settings = z.infer<typeof settingsSchema>;

View File

@ -29,4 +29,4 @@ export const twitter = (username: string) => {
export const facebook = (username: string) => { export const facebook = (username: string) => {
// Handles a starting slash, this shouldn't happen, but just in case // Handles a starting slash, this shouldn't happen, but just in case
return `https://www.facebook.com/${username.replace(/^\//, "")}`; return `https://www.facebook.com/${username.replace(/^\//, "")}`;
}; };

View File

@ -31,4 +31,4 @@ export type Tag = z.infer<typeof tagsSchema>;
export const tagsIncludeSchema = z.object({ export const tagsIncludeSchema = z.object({
"count.posts": z.literal(true).optional(), "count.posts": z.literal(true).optional(),
}); });
export type TagsIncludeSchema = z.infer<typeof tagsIncludeSchema>; export type TagsIncludeSchema = z.infer<typeof tagsIncludeSchema>;

View File

@ -37,4 +37,4 @@ export const tiersIncludeSchema = z.object({
yearly_price: z.literal(true).optional(), yearly_price: z.literal(true).optional(),
benefits: z.literal(true).optional(), benefits: z.literal(true).optional(),
}); });
export type TiersIncludeSchema = z.infer<typeof tiersIncludeSchema>; export type TiersIncludeSchema = z.infer<typeof tiersIncludeSchema>;

View File

@ -1,54 +1,54 @@
import { expect, describe, it } from "vitest"; import { describe, expect, it } from "vitest";
import { GhostUserConfigSchema } from "./userconfig"; import { GhostUserConfigSchema } from "./userconfig";
describe("GhostUserConfigSchema", () => { describe("GhostUserConfigSchema", () => {
it("should validate a valid user config", () => { it("should validate a valid user config", () => {
const validConfig = { const validConfig = {
ghostURL: "https://ghostdemo.matthiesen.xyz", ghostURL: "https://ghostdemo.matthiesen.xyz",
disableThemeProvider: true, disableThemeProvider: true,
ThemeProvider: { ThemeProvider: {
theme: "@matthiesenxyz/astro-ghostcms-theme-default", theme: "@matthiesenxyz/astro-ghostcms-theme-default",
}, },
disableDefault404: false, disableDefault404: false,
enableRSSFeed: true, enableRSSFeed: true,
enableOGImages: true, enableOGImages: true,
sitemap: { sitemap: {
// sitemap configuration // sitemap configuration
}, },
robotstxt: { robotstxt: {
// robotstxt configuration // robotstxt configuration
}, },
fullConsoleLogs: false, fullConsoleLogs: false,
}; };
const result = GhostUserConfigSchema.safeParse(validConfig); const result = GhostUserConfigSchema.safeParse(validConfig);
expect(result.success).to.be.true; expect(result.success).to.be.true;
expect(result.data).to.deep.equal(validConfig); expect(result.data).to.deep.equal(validConfig);
}); });
it("should invalidate an invalid user config", () => { it("should invalidate an invalid user config", () => {
const invalidConfig = { const invalidConfig = {
ghostURL: "invalid-url", ghostURL: "invalid-url",
disableThemeProvider: "true", disableThemeProvider: "true",
ThemeProvider: { ThemeProvider: {
theme: 123, theme: 123,
}, },
disableDefault404: "false", disableDefault404: "false",
enableRSSFeed: "true", enableRSSFeed: "true",
enableOGImages: "true", enableOGImages: "true",
sitemap: { sitemap: {
// invalid sitemap configuration // invalid sitemap configuration
}, },
robotstxt: { robotstxt: {
// invalid robotstxt configuration // invalid robotstxt configuration
}, },
fullConsoleLogs: "false", fullConsoleLogs: "false",
}; };
const result = GhostUserConfigSchema.safeParse(invalidConfig); const result = GhostUserConfigSchema.safeParse(invalidConfig);
expect(result.success).to.be.false; expect(result.success).to.be.false;
expect(!result.success).to.exist; expect(!result.success).to.exist;
}); });
}); });

View File

@ -18,12 +18,17 @@ export const GhostUserConfigSchema = z.object({
* @default false * @default false
*/ */
disableThemeProvider: z.boolean().optional().default(false), disableThemeProvider: z.boolean().optional().default(false),
ThemeProvider: z.object({ ThemeProvider: z
/** OPTIONAL - Set the theme you want to use .object({
* @default "@matthiesenxyz/astro-ghostcms-theme-default" /** OPTIONAL - Set the theme you want to use
*/ * @default "@matthiesenxyz/astro-ghostcms-theme-default"
theme: z.string().optional().default("@matthiesenxyz/astro-ghostcms-theme-default"), */
}).optional(), theme: z
.string()
.optional()
.default("@matthiesenxyz/astro-ghostcms-theme-default"),
})
.optional(),
/** Allows the user to disable the provided 404 page */ /** Allows the user to disable the provided 404 page */
disableDefault404: z.boolean().optional().default(false), disableDefault404: z.boolean().optional().default(false),
/** Allows the user to disable the provided RSS Feed */ /** Allows the user to disable the provided RSS Feed */

View File

@ -23,4 +23,4 @@ export default async function latestVersion(
const { version } = await packageJson(packageName.toLowerCase(), options); const { version } = await packageJson(packageName.toLowerCase(), options);
return version; return version;
} }

View File

@ -1,4 +1,4 @@
declare module "virtual:@matthiesenxyz/astro-ghostcms/config" { declare module "virtual:@matthiesenxyz/astro-ghostcms/config" {
const Config: import("./src/schemas/userconfig").GhostUserConfig; const Config: import("./src/schemas/userconfig").GhostUserConfig;
export default Config; export default Config;
} }

View File

@ -1,6 +1,6 @@
/// <reference types="vitest" /> /// <reference types="vitest" />
/// <reference types="vite/client" /> /// <reference types="vite/client" />
import { defineProject } from 'vitest/config' import { defineProject } from "vitest/config";
export default defineProject({ export default defineProject({
test: { test: {
@ -9,4 +9,4 @@ export default defineProject({
watchExclude: [".*\\/node_modules\\/.*", ".*\\/build\\/.*"], watchExclude: [".*\\/node_modules\\/.*", ".*\\/build\\/.*"],
exclude: ["node_modules", "dist", ".idea", ".git", ".cache"], exclude: ["node_modules", "dist", ".idea", ".git", ".cache"],
}, },
}) });

View File

@ -1,16 +1,16 @@
import { defineConfig } from "astro/config";
import ghostcms from "@matthiesenxyz/astro-ghostcms"; import ghostcms from "@matthiesenxyz/astro-ghostcms";
import UnoCSS from 'unocss/astro'; import { defineConfig } from "astro/config";
import UnoCSS from "unocss/astro";
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
site: "https://example.xyz/", site: "https://example.xyz/",
trailingSlash: 'ignore', trailingSlash: "ignore",
integrations: [ integrations: [
UnoCSS({ injectReset: true }), UnoCSS({ injectReset: true }),
ghostcms({ ghostcms({
theme: "@matthiesenxyz/astro-ghostcms-brutalbyelian", theme: "@matthiesenxyz/astro-ghostcms-brutalbyelian",
ghostURL: "https://ghostdemo.matthiesen.xyz", ghostURL: "https://ghostdemo.matthiesen.xyz",
}) }),
], ],
}); });

View File

@ -1,9 +1,9 @@
/// <reference types="astro/client" /> /// <reference types="astro/client" />
interface ImportMetaEnv { interface ImportMetaEnv {
readonly CONTENT_API_KEY: string readonly CONTENT_API_KEY: string;
readonly CONTENT_API_URL: string readonly CONTENT_API_URL: string;
} }
interface ImportMeta { interface ImportMeta {
readonly env: ImportMetaEnv; readonly env: ImportMetaEnv;
} }

View File

@ -1,6 +1,6 @@
import brutalTheme from '@matthiesenxyz/astro-ghostcms-brutalbyelian'; import brutalTheme from "@matthiesenxyz/astro-ghostcms-brutalbyelian";
import { defineConfig } from 'unocss'; import { defineConfig } from "unocss";
export default defineConfig({ export default defineConfig({
presets: [ brutalTheme() ], presets: [brutalTheme()],
}); });

View File

@ -1,91 +1,126 @@
import type { StarlightPlugin, StarlightUserConfig } from '@astrojs/starlight/types' import type {
import type { AstroIntegrationLogger } from 'astro' StarlightPlugin,
import { type StarlightGhostConfig, validateConfig } from './src/schemas/config' StarlightUserConfig,
import { vitePluginStarlightGhostConfig } from './src/integrations/vite' } from "@astrojs/starlight/types";
import { facebook, getSettings, invariant, twitter } from './src/utils/api' import type { AstroIntegrationLogger } from "astro";
import { vitePluginStarlightGhostConfig } from "./src/integrations/vite";
import {
type StarlightGhostConfig,
validateConfig,
} from "./src/schemas/config";
import { facebook, getSettings, invariant, twitter } from "./src/utils/api";
const settings = await getSettings() const settings = await getSettings();
export type { StarlightGhostConfig } export type { StarlightGhostConfig };
export default function starlightGhostCMS(userConfig?: StarlightGhostConfig): StarlightPlugin { export default function starlightGhostCMS(
const config: StarlightGhostConfig = validateConfig(userConfig) userConfig?: StarlightGhostConfig,
invariant(settings, "Settings not available... check your api key/url") ): StarlightPlugin {
const config: StarlightGhostConfig = validateConfig(userConfig);
invariant(settings, "Settings not available... check your api key/url");
return { return {
name: '@matthiesenxyz/starlight-ghostcms-plugin', name: "@matthiesenxyz/starlight-ghostcms-plugin",
hooks: { hooks: {
setup({ astroConfig, addIntegration, config: starlightConfig, logger, updateConfig: updateStarlightConfig }) { setup({
updateStarlightConfig({ astroConfig,
social: { addIntegration,
...starlightConfig.social, config: starlightConfig,
rss: `${astroConfig.site}/rss.xml`, logger,
twitter: twitter(settings.twitter?settings.twitter:""), updateConfig: updateStarlightConfig,
facebook: facebook(settings.facebook?settings.facebook:""), }) {
}, updateStarlightConfig({
components: { social: {
...starlightConfig.components, ...starlightConfig.social,
...overrideStarlightComponent(starlightConfig.components, logger, 'MarkdownContent'), rss: `${astroConfig.site}/rss.xml`,
...overrideStarlightComponent(starlightConfig.components, logger, 'Sidebar'), twitter: twitter(settings.twitter ? settings.twitter : ""),
...overrideStarlightComponent(starlightConfig.components, logger, "SiteTitle"), facebook: facebook(settings.facebook ? settings.facebook : ""),
} },
}) components: {
...starlightConfig.components,
...overrideStarlightComponent(
starlightConfig.components,
logger,
"MarkdownContent",
),
...overrideStarlightComponent(
starlightConfig.components,
logger,
"Sidebar",
),
...overrideStarlightComponent(
starlightConfig.components,
logger,
"SiteTitle",
),
},
});
addIntegration({ addIntegration({
name: '@matthiesenxyz/starlight-ghostcms', name: "@matthiesenxyz/starlight-ghostcms",
hooks: { hooks: {
'astro:config:setup': ({ injectRoute, updateConfig }) => { "astro:config:setup": ({ injectRoute, updateConfig }) => {
injectRoute({ injectRoute({
pattern: '/blog', pattern: "/blog",
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/index.astro', entrypoint:
prerender: true, "@matthiesenxyz/starlight-ghostcms/routes/index.astro",
}) prerender: true,
injectRoute({ });
pattern: '/blog/[slug]', injectRoute({
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/[slug].astro', pattern: "/blog/[slug]",
prerender: true, entrypoint:
}) "@matthiesenxyz/starlight-ghostcms/routes/[slug].astro",
injectRoute({ prerender: true,
pattern: '/blog/about', });
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/about.astro', injectRoute({
prerender: true, pattern: "/blog/about",
}) entrypoint:
injectRoute({ "@matthiesenxyz/starlight-ghostcms/routes/about.astro",
pattern: '/blog/authors', prerender: true,
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/authors.astro', });
}) injectRoute({
injectRoute({ pattern: "/blog/authors",
pattern: '/rss.xml', entrypoint:
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/rss.xml.ts' "@matthiesenxyz/starlight-ghostcms/routes/authors.astro",
}) });
injectRoute({
pattern: "/rss.xml",
entrypoint:
"@matthiesenxyz/starlight-ghostcms/routes/rss.xml.ts",
});
updateConfig({ updateConfig({
vite: { vite: {
plugins: [vitePluginStarlightGhostConfig(config)], plugins: [vitePluginStarlightGhostConfig(config)],
}, },
}) });
} },
} },
}) });
} },
}, },
} };
} }
function overrideStarlightComponent( function overrideStarlightComponent(
components: StarlightUserConfig['components'], components: StarlightUserConfig["components"],
logger: AstroIntegrationLogger, logger: AstroIntegrationLogger,
component: keyof NonNullable<StarlightUserConfig['components']>, component: keyof NonNullable<StarlightUserConfig["components"]>,
) { ) {
if (components?.[component]) { if (components?.[component]) {
logger.warn(`It looks like you already have a \`${component}\` component override in your Starlight configuration.`) logger.warn(
logger.warn(`To use \`starlight-ghostcms\`, remove the override for the \`${component}\` component.\n`) `It looks like you already have a \`${component}\` component override in your Starlight configuration.`,
logger.warn("This Warning can be ignored if you know what your doing ;)") );
logger.warn(
return {} `To use \`starlight-ghostcms\`, remove the override for the \`${component}\` component.\n`,
} );
logger.warn("This Warning can be ignored if you know what your doing ;)");
return {
[component]: `@matthiesenxyz/starlight-ghostcms/overrides/${component}.astro`, return {};
} }
}
return {
[component]: `@matthiesenxyz/starlight-ghostcms/overrides/${component}.astro`,
};
}

View File

@ -1,22 +1,24 @@
import type { ViteUserConfig } from 'astro' import type { ViteUserConfig } from "astro";
import type { StarlightGhostConfig } from '../schemas/config.ts' import type { StarlightGhostConfig } from "../schemas/config.ts";
// Expose the starlight-blog plugin configuration. // Expose the starlight-blog plugin configuration.
export function vitePluginStarlightGhostConfig(config: StarlightGhostConfig): VitePlugin { export function vitePluginStarlightGhostConfig(
const moduleId = 'virtual:starlight-ghost-config' config: StarlightGhostConfig,
const resolvedModuleId = `\0${moduleId}` ): VitePlugin {
const moduleContent = `export default ${JSON.stringify(config)}` const moduleId = "virtual:starlight-ghost-config";
const resolvedModuleId = `\0${moduleId}`;
const moduleContent = `export default ${JSON.stringify(config)}`;
return { return {
name: 'vite-plugin-starlight-ghost-config', name: "vite-plugin-starlight-ghost-config",
load(id) { load(id) {
return id === resolvedModuleId ? moduleContent : undefined return id === resolvedModuleId ? moduleContent : undefined;
}, },
resolveId(id) { resolveId(id) {
return id === moduleId ? resolvedModuleId : undefined return id === moduleId ? resolvedModuleId : undefined;
}, },
} };
} }
type VitePlugin = NonNullable<ViteUserConfig['plugins']>[number] type VitePlugin = NonNullable<ViteUserConfig["plugins"]>[number];

View File

@ -1,15 +1,19 @@
import { z } from 'astro/zod'; import type { AstroBuiltinAttributes } from "astro";
import type { AstroBuiltinAttributes } from 'astro'; import type { HTMLAttributes } from "astro/types";
import type { HTMLAttributes } from 'astro/types'; import { z } from "astro/zod";
const linkHTMLAttributesSchema = z.record( const linkHTMLAttributesSchema = z.record(
z.union([z.string(), z.number(), z.boolean(), z.undefined()]) z.union([z.string(), z.number(), z.boolean(), z.undefined()]),
) as z.Schema<Omit<HTMLAttributes<'a'>, keyof AstroBuiltinAttributes | 'children'>>; ) as z.Schema<
Omit<HTMLAttributes<"a">, keyof AstroBuiltinAttributes | "children">
>;
export type LinkHTMLAttributes = z.infer<typeof linkHTMLAttributesSchema>; export type LinkHTMLAttributes = z.infer<typeof linkHTMLAttributesSchema>;
const badgeSchema = () => const badgeSchema = () =>
z.object({ z.object({
variant: z.enum(['note', 'danger', 'success', 'caution', 'tip', 'default']).default('default'), variant: z
.enum(["note", "danger", "success", "caution", "tip", "default"])
.default("default"),
text: z.string(), text: z.string(),
}); });
@ -17,8 +21,8 @@ export const BadgeConfigSchema = () =>
z z
.union([z.string(), badgeSchema()]) .union([z.string(), badgeSchema()])
.transform((badge) => { .transform((badge) => {
if (typeof badge === 'string') { if (typeof badge === "string") {
return { variant: 'default' as const, text: badge }; return { variant: "default" as const, text: badge };
} }
return badge; return badge;
}) })
@ -27,7 +31,7 @@ export const BadgeConfigSchema = () =>
export type Badge = z.output<ReturnType<typeof badgeSchema>>; export type Badge = z.output<ReturnType<typeof badgeSchema>>;
export interface Link { export interface Link {
type: 'link'; type: "link";
label: string; label: string;
href: string; href: string;
isCurrent: boolean; isCurrent: boolean;
@ -36,11 +40,11 @@ export interface Link {
} }
interface Group { interface Group {
type: 'group'; type: "group";
label: string; label: string;
entries: (Link | Group)[]; entries: (Link | Group)[];
collapsed: boolean; collapsed: boolean;
badge: Badge | undefined; badge: Badge | undefined;
} }
export type SidebarEntry = Link | Group; export type SidebarEntry = Link | Group;

View File

@ -5,17 +5,17 @@ import { getAllPosts, getSettings, invariant } from "../utils/api";
const posts = await getAllPosts(); const posts = await getAllPosts();
const settings = await getSettings(); const settings = await getSettings();
import config from 'virtual:starlight-ghost-config'; import config from "virtual:starlight-ghost-config";
export async function GET({ site }: APIContext) { export async function GET({ site }: APIContext) {
invariant(settings,"Settings is not defined") invariant(settings, "Settings is not defined");
const title = config.title; const title = config.title;
const description = config.rssDescription; const description = config.rssDescription;
const ghostSite = settings.url const ghostSite = settings.url;
return rss({ return rss({
title: title, title: title,
description: description, description: description,
site: site?site:ghostSite, site: site ? site : ghostSite,
items: posts.map((post) => ({ items: posts.map((post) => ({
title: post.title, title: post.title,
pubDate: new Date( pubDate: new Date(

View File

@ -1,50 +1,52 @@
import { AstroError } from 'astro/errors' import { AstroError } from "astro/errors";
import { z } from 'astro/zod' import { z } from "astro/zod";
const configSchema = z const configSchema = z
.object({ .object({
/** /**
* The number of blog posts to display per page in the blog post list. * The number of blog posts to display per page in the blog post list.
*/ */
postCount: z.number().min(1).default(5), postCount: z.number().min(1).default(5),
/** /**
* The number of recent blog posts to display in the sidebar. * The number of recent blog posts to display in the sidebar.
*/ */
recentPostCount: z.number().min(1).default(10), recentPostCount: z.number().min(1).default(10),
/** /**
* The title of the blog. * The title of the blog.
*/ */
title: z.string().default('Blog'), title: z.string().default("Blog"),
/** /**
* The description of the blog on the RSS Feed. * The description of the blog on the RSS Feed.
*/ */
rssDescription: z.string().default('My Awesome Starlight-GhostCMS Blog'), rssDescription: z.string().default("My Awesome Starlight-GhostCMS Blog"),
/** /**
* Turn on and off "Powered by Ghost" * Turn on and off "Powered by Ghost"
*/ */
supportGhost: z.boolean().default(true), supportGhost: z.boolean().default(true),
}) })
.default({}) .default({});
export function validateConfig(userConfig: unknown): StarlightGhostConfig { export function validateConfig(userConfig: unknown): StarlightGhostConfig {
const config = configSchema.safeParse(userConfig) const config = configSchema.safeParse(userConfig);
if (!config.success) { if (!config.success) {
const errors = config.error.flatten() const errors = config.error.flatten();
throw new AstroError( throw new AstroError(
`Invalid starlight-GhostCMS configuration: `Invalid starlight-GhostCMS configuration:
${errors.formErrors.map((formError) => ` - ${formError}`).join('\n')} ${errors.formErrors.map((formError) => ` - ${formError}`).join("\n")}
${Object.entries(errors.fieldErrors) ${Object.entries(errors.fieldErrors)
.map(([fieldName, fieldErrors]) => ` - ${fieldName}: ${fieldErrors.join(' - ')}`) .map(
.join('\n')} ([fieldName, fieldErrors]) => ` - ${fieldName}: ${fieldErrors.join(" - ")}`,
)
.join("\n")}
`, `,
"See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/matthiesenxyz/astro-ghostcms/issues/new/choose", "See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/matthiesenxyz/astro-ghostcms/issues/new/choose",
) );
} }
return config.data return config.data;
} }
export type StarlightGhostConfig = z.infer<typeof configSchema> export type StarlightGhostConfig = z.infer<typeof configSchema>;

View File

@ -1,8 +1,8 @@
import { TS_API } from "./content-api";
import type { Page, Post } from "./content-api/schemas";
import type { ContentAPICredentials } from './content-api/content-api'
// LOAD ENVIRONMENT VARIABLES // LOAD ENVIRONMENT VARIABLES
import { loadEnv } from "vite"; import { loadEnv } from "vite";
import { TS_API } from "./content-api";
import type { ContentAPICredentials } from "./content-api/content-api";
import type { Page, Post } from "./content-api/schemas";
import { invariant } from "./invariant.js"; import { invariant } from "./invariant.js";
const { CONTENT_API_KEY, CONTENT_API_URL } = loadEnv( const { CONTENT_API_KEY, CONTENT_API_URL } = loadEnv(
@ -11,17 +11,11 @@ const { CONTENT_API_KEY, CONTENT_API_URL } = loadEnv(
"CONTENT_", "CONTENT_",
); );
invariant( invariant(CONTENT_API_KEY, "CONTENT_API_KEY Missing from .env");
CONTENT_API_KEY, invariant(CONTENT_API_URL, "CONTENT_API_URL Missing from .env");
"CONTENT_API_KEY Missing from .env"
)
invariant(
CONTENT_API_URL,
"CONTENT_API_URL Missing from .env"
)
const key:ContentAPICredentials["key"] = CONTENT_API_KEY; const key: ContentAPICredentials["key"] = CONTENT_API_KEY;
const url:ContentAPICredentials["url"] = CONTENT_API_URL; const url: ContentAPICredentials["url"] = CONTENT_API_URL;
const version = "v5.0"; const version = "v5.0";
const api = new TS_API(url, key, version); const api = new TS_API(url, key, version);
@ -73,20 +67,21 @@ export const getAllPosts = async () => {
return posts; return posts;
}; };
export const getSluggedPost = async (slug:string) => { export const getSluggedPost = async (slug: string) => {
const results = await api.posts const results = await api.posts
.read({slug: slug}) .read({ slug: slug })
.include({ .include({
authors: true, authors: true,
tags: true, tags: true,
}).fetch() })
.fetch();
if (!results.success) {
throw new Error(results.errors.map((e) => e.message).join(", ")); if (!results.success) {
} throw new Error(results.errors.map((e) => e.message).join(", "));
return { }
post: results.data, return {
}; post: results.data,
};
}; };
export const getAllPages = async () => { export const getAllPages = async () => {
@ -106,20 +101,21 @@ export const getAllPages = async () => {
return pages; return pages;
}; };
export const getSluggedPage = async (slug:string) => { export const getSluggedPage = async (slug: string) => {
const results = await api.pages const results = await api.pages
.read({slug: slug}) .read({ slug: slug })
.include({ .include({
authors: true, authors: true,
tags: true, tags: true,
}).fetch() })
.fetch();
if (!results.success) {
throw new Error(results.errors.map((e) => e.message).join(", ")); if (!results.success) {
} throw new Error(results.errors.map((e) => e.message).join(", "));
return { }
post: results.data, return {
}; post: results.data,
};
}; };
export const getSettings = async () => { export const getSettings = async () => {

View File

@ -1,33 +1,33 @@
export function isAnyBlogPage(slug: string) { export function isAnyBlogPage(slug: string) {
return slug.match(/^blog(\/?$|\/.+\/?$)/) !== null return slug.match(/^blog(\/?$|\/.+\/?$)/) !== null;
} }
export function isBlogRoot(slug: string) { export function isBlogRoot(slug: string) {
return slug === 'blog' return slug === "blog";
} }
export function isAnyBlogPostPage(slug: string) { export function isAnyBlogPostPage(slug: string) {
return slug.match(/^blog\/(?!(\d+\/?|tags\/.+)$).+$/) !== null return slug.match(/^blog\/(?!(\d+\/?|tags\/.+)$).+$/) !== null;
} }
export function isBlogPostPage(slug: string, postSlug: string) { export function isBlogPostPage(slug: string, postSlug: string) {
return slug === postSlug return slug === postSlug;
} }
export function isBlogTagsPage(slug: string, tag: string) { export function isBlogTagsPage(slug: string, tag: string) {
return slug === `blog/tags/${tag}` return slug === `blog/tags/${tag}`;
} }
export function getPageProps(title: string): StarlightPageProps { export function getPageProps(title: string): StarlightPageProps {
return { return {
frontmatter: { frontmatter: {
title, title,
}, },
} };
} }
interface StarlightPageProps { interface StarlightPageProps {
frontmatter: { frontmatter: {
title: string title: string;
} };
} }

View File

@ -1,5 +1,5 @@
declare module 'virtual:starlight/user-config' { declare module "virtual:starlight/user-config" {
const Config: import('@astrojs/starlight/types').StarlightConfig const Config: import("@astrojs/starlight/types").StarlightConfig;
export default Config export default Config;
} }

View File

@ -1,5 +1,5 @@
declare module 'virtual:starlight-ghost-config' { declare module "virtual:starlight-ghost-config" {
const StarlightGhostConfig: import('./src/schemas/config').StarlightGhostConfig const StarlightGhostConfig: import("./src/schemas/config").StarlightGhostConfig;
export default StarlightGhostConfig export default StarlightGhostConfig;
} }

View File

@ -12,7 +12,7 @@ export default defineConfig({
ghostcms({ ghostcms({
ghostURL: "https://ghostdemo.matthiesen.xyz", ghostURL: "https://ghostdemo.matthiesen.xyz",
ThemeProvider: { ThemeProvider: {
theme: '@matthiesenxyz/astro-ghostcms-brutalbyelian', theme: "@matthiesenxyz/astro-ghostcms-brutalbyelian",
}, },
}), }),
], ],

View File

@ -1,4 +1,4 @@
{ {
"recommendations": ["astro-build.astro-vscode"], "recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": [] "unwantedRecommendations": []
} }

View File

@ -1,11 +1,11 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"command": "./node_modules/.bin/astro dev", "command": "./node_modules/.bin/astro dev",
"name": "Development server", "name": "Development server",
"request": "launch", "request": "launch",
"type": "node-terminal" "type": "node-terminal"
} }
] ]
} }

View File

@ -1,35 +1,35 @@
import { defineConfig } from 'astro/config'; import starlight from "@astrojs/starlight";
import starlight from '@astrojs/starlight'; import starlightGhostCMS from "@matthiesenxyz/starlight-ghostcms";
import starlightGhostCMS from '@matthiesenxyz/starlight-ghostcms'; import { defineConfig } from "astro/config";
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
site: "http://localhost:4321", site: "http://localhost:4321",
integrations: [ integrations: [
starlight({ starlight({
title: 'My Docs', title: "My Docs",
plugins: [ plugins: [
starlightGhostCMS({ starlightGhostCMS({
title: "Demo Blog", title: "Demo Blog",
rssDescription: "Starlight Playground" rssDescription: "Starlight Playground",
}) }),
], ],
social: { social: {
github: 'https://github.com/withastro/starlight', github: "https://github.com/withastro/starlight",
}, },
sidebar: [ sidebar: [
{ {
label: 'Guides', label: "Guides",
items: [ items: [
// Each item here is one entry in the navigation menu. // Each item here is one entry in the navigation menu.
{ label: 'Example Guide', link: '/guides/example/' }, { label: "Example Guide", link: "/guides/example/" },
], ],
}, },
{ {
label: 'Reference', label: "Reference",
autogenerate: { directory: 'reference' }, autogenerate: { directory: "reference" },
}, },
], ],
}), }),
], ],
}); });

View File

@ -1,6 +1,6 @@
import { defineCollection } from 'astro:content'; import { docsSchema } from "@astrojs/starlight/schema";
import { docsSchema } from '@astrojs/starlight/schema'; import { defineCollection } from "astro:content";
export const collections = { export const collections = {
docs: defineCollection({ schema: docsSchema() }), docs: defineCollection({ schema: docsSchema() }),
}; };

View File

@ -1,2 +1,2 @@
/// <reference path="../.astro/types.d.ts" /> /// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" /> /// <reference types="astro/client" />