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

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

View File

@ -4,10 +4,7 @@ import type { Page, Post } from "../schemas/api";
// LOAD ENVIRONMENT VARIABLES
import { loadEnv } from "vite";
const {
CONTENT_API_KEY,
CONTENT_API_URL
} = loadEnv(
const { CONTENT_API_KEY, CONTENT_API_URL } = loadEnv(
"all",
process.cwd(),
"CONTENT_",

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 { 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 c from "picocolors";
import { loadEnv } from "vite";
import { GhostUserConfigSchema } from "./schemas/userconfig";
import latestVersion from "./utils/latestVersion";
// Import External Integrations
import sitemap from "@astrojs/sitemap";
import robotsTxt from "astro-robots-txt";
import ghostRSS from "./integrations/rssfeed";
// Import Internal Integrations
import ghostOGImages from "./integrations/satoriog"
import ghostRSS from "./integrations/rssfeed"
import ghostThemeProvider from "./integrations/themeprovider"
import ghostOGImages from "./integrations/satoriog";
import ghostThemeProvider from "./integrations/themeprovider";
// Load environment variables
const ENV = loadEnv("all", process.cwd(), "CONTENT_API");
@ -40,112 +40,184 @@ export default defineIntegration({
addVirtualImports,
addDts,
injectRoute,
logger
logger,
}) => {
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 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')}`);
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 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())
GhostLogger.info('Initializing @matthiesenxyz/astro-ghostcms...')
watchIntegration(resolve());
GhostLogger.info("Initializing @matthiesenxyz/astro-ghostcms...");
const verbose = options.fullConsoleLogs;
// Check for GhostCMS environment variables
GhostENVLogger.info(c.bold(c.yellow('Checking for GhostCMS environment variables & user configuration')))
GhostENVLogger.info(
c.bold(
c.yellow(
"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`)
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) {
GhostENVLogger.warn(c.bgYellow(c.bold(c.black('ghostURL is not set in user configuration falling back to environment variable'))))
GhostENVLogger.warn(
c.bgYellow(
c.bold(
c.black(
"ghostURL is not set in user configuration falling back to environment variable",
),
),
),
);
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.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')))
GhostENVLogger.info(
c.bold(c.green("GhostCMS environment variables are set")),
);
// 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
if (!options.disableThemeProvider) {
addIntegration(
ghostThemeProvider({
theme: options.ThemeProvider?.theme,
verbose
})
)
verbose,
}),
);
} else {
if (verbose) {
GhostIntegrationLogger.info(c.gray('Theme Provider is disabled'))
GhostIntegrationLogger.info(c.gray("Theme Provider is disabled"));
}
}
// Satori OG Images
if (options.enableOGImages) {
addIntegration(ghostOGImages({ verbose }))
addIntegration(ghostOGImages({ verbose }));
} else {
if (verbose) {
GhostIntegrationLogger.info(c.gray('OG Image Provider is disabled'))
GhostIntegrationLogger.info(
c.gray("OG Image Provider is disabled"),
);
}
}
// RSS Feed
if (options.enableRSSFeed) {
addIntegration(ghostRSS({ verbose }))
addIntegration(ghostRSS({ verbose }));
} else {
if (verbose) {
GhostIntegrationLogger.info(c.gray('RSS Feed is disabled'))
GhostIntegrationLogger.info(c.gray("RSS Feed is disabled"));
}
}
// @ASTROJS/SITEMAP
if (!hasIntegration("@astrojs/sitemap")) {
if (verbose) {
GhostIntegrationLogger.info(c.bold(c.magenta(`Adding ${c.blue("@astrojs/sitemap")} integration`)))
GhostIntegrationLogger.info(
c.bold(
c.magenta(`Adding ${c.blue("@astrojs/sitemap")} integration`),
),
);
}
addIntegration(sitemap())
addIntegration(sitemap());
} else {
if (verbose) {
GhostIntegrationLogger.info(c.gray('@astrojs/sitemap integration already exists, skipping...'))
GhostIntegrationLogger.info(
c.gray(
"@astrojs/sitemap 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`)))
GhostIntegrationLogger.info(
c.bold(
c.magenta(`Adding ${c.blue("astro-robots-txt")} integration`),
),
);
}
addIntegration(robotsTxt())
addIntegration(robotsTxt());
} else {
if (verbose) {
GhostIntegrationLogger.info(c.gray('astro-robots-txt integration already exists, skipping...'))
GhostIntegrationLogger.info(
c.gray(
"astro-robots-txt integration already exists, skipping...",
),
);
}
}
// Set up default 404 page
if (!options.disableDefault404) {
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`,
prerender: true
})
prerender: true,
});
} else {
if (verbose) {
GhostRouteLogger.info(c.gray('Default 404 page is disabled, Skipping...'))
GhostRouteLogger.info(
c.gray("Default 404 page is disabled, Skipping..."),
);
}
}
// Add virtual imports for user configuration
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
addDts({
@ -153,37 +225,82 @@ export default defineIntegration({
content: `declare module "virtual:@matthiesenxyz/astro-ghostcms/config" {
const Config: import("../schemas/userconfig").GhostUserConfig;
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')))
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'))}`);
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
GhostLogger.info(c.bold(c.magenta('Running Astro-GhostCMS in Deveopment mode 🚀')))
GhostLogger.info(
c.bold(c.magenta("Running Astro-GhostCMS in Deveopment mode 🚀")),
);
// Check for updates
const currentNPMVersion = await latestVersion("@matthiesenxyz/astro-ghostcms");
const packageJson = await fse.readJson(path.resolve(fileURLToPath(import.meta.url), "../../package.json"));
const currentNPMVersion = await latestVersion(
"@matthiesenxyz/astro-ghostcms",
);
const packageJson = await fse.readJson(
path.resolve(fileURLToPath(import.meta.url), "../../package.json"),
);
const localVersion = packageJson.version;
if (currentNPMVersion !== localVersion) {
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`)
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}`)))
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 🚀')))
}
}
}
})
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;

View File

@ -1,7 +1,7 @@
import { createResolver, defineIntegration } from "astro-integration-kit";
import { corePlugins } from "astro-integration-kit/plugins";
import c from "picocolors";
import { z } from "astro/zod";
import c from "picocolors";
export default defineIntegration({
name: "@matthiesenxyz/astro-ghostcms-rss",
@ -13,17 +13,17 @@ export default defineIntegration({
const { resolve } = createResolver(import.meta.url);
return {
"astro:config:setup": ({
watchIntegration,
injectRoute,
logger
}) => {
watchIntegration(resolve())
const RSSLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('RSSGenerator')}`);
"astro:config:setup": ({ watchIntegration, injectRoute, logger }) => {
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({
pattern: "/rss-style.xsl",
@ -34,15 +34,18 @@ export default defineIntegration({
pattern: "/rss.xml",
entrypoint: `${rssRoute}/rss.xml.ts`,
});
},
"astro:config:done": ({ logger }) => {
const RSSLogger = logger.fork(`${c.bold(c.blue('👻 Astro-GhostCMS'))}${c.gray("/")}${c.blue('RSSGenerator')}`);
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')))
RSSLogger.info(c.bold(c.green("RSS Feed Setup Complete")));
}
}
}
}
})
},
};
},
});

View File

@ -1,7 +1,7 @@
import { createResolver, defineIntegration } from "astro-integration-kit";
import { corePlugins } from "astro-integration-kit/plugins";
import c from "picocolors";
import { z } from "astro/zod";
import c from "picocolors";
export default defineIntegration({
name: "@matthiesenxyz/astro-ghostcms-satoriog",
@ -17,15 +17,21 @@ export default defineIntegration({
watchIntegration,
updateConfig,
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({
pattern: "/open-graph/[slug].png",
@ -58,16 +64,22 @@ export default defineIntegration({
});
updateConfig({
vite: { optimizeDeps: { exclude: ["@resvg/resvg-js"] } }
})
vite: { optimizeDeps: { exclude: ["@resvg/resvg-js"] } },
});
},
"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) {
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,
} from "astro";
import { html } from "satori-html";
import {
getAllPages,
getAllPosts,
getSettings,
invariant,
} from "../../../api";
import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api";
import satoriOG from "../satori";
export const getStaticPaths: GetStaticPaths = async () => {

View File

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

View File

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

View File

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

View File

@ -6,7 +6,10 @@ import c from "picocolors";
export default defineIntegration({
name: "@matthiesenxyz/astro-ghostcms-themeprovider",
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),
}),
plugins: [...corePlugins],
@ -14,22 +17,30 @@ export default defineIntegration({
const { resolve } = createResolver(import.meta.url);
return {
"astro:config:setup": ({
watchIntegration,
injectRoute,
logger
}) => {
watchIntegration(resolve())
"astro:config:setup": ({ watchIntegration, 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.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 {
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)),
)}`,
);
}
}
@ -67,15 +78,18 @@ export default defineIntegration({
pattern: "/archives/[...page]",
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')}`);
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')))
themeLogger.info(c.bold(c.green("Provider Setup Complete")));
}
}
}
}
})
},
};
},
});

View File

@ -1,5 +1,5 @@
import createFetchMock from "vitest-fetch-mock";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import createFetchMock from "vitest-fetch-mock";
import { TSGhostContentAPI } from "@ts-ghost/content-api";
@ -7,7 +7,8 @@ const fetchMocker = createFetchMock(vi);
describe("authors api .browse() Args Type-safety", () => {
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";
const api = new TSGhostContentAPI(url, key, "v5.0");
test(".browse() params shouldnt accept invalid params", () => {
// @ts-expect-error - shouldnt accept invalid params
@ -19,19 +20,28 @@ describe("authors api .browse() Args Type-safety", () => {
// @ts-expect-error - order should ony contain field
expect(() => api.authors.browse({ order: "foo ASC" })).toThrow();
// valid
expect(api.authors.browse({ order: "name ASC" }).getParams().browseParams).toStrictEqual({
expect(
api.authors.browse({ order: "name ASC" }).getParams().browseParams,
).toStrictEqual({
order: "name ASC",
});
expect(api.authors.browse({ order: "name ASC,slug DESC" }).getParams().browseParams).toStrictEqual({
expect(
api.authors.browse({ order: "name ASC,slug DESC" }).getParams()
.browseParams,
).toStrictEqual({
order: "name ASC,slug DESC",
});
expect(
api.authors.browse({ order: "name ASC,slug DESC,location ASC" }).getParams().browseParams
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();
expect(() =>
api.authors.browse({ order: "name ASC,slug DESC,locaton ASC" }),
).toThrow();
});
test(".browse() 'filter' params should ony accept valid field", () => {
@ -39,14 +49,14 @@ describe("authors api .browse() Args Type-safety", () => {
api.authors.browse({
// @ts-expect-error - order should ony contain field
filter: "foo:bar",
})
}),
).toThrow();
expect(
api.authors
.browse({
filter: "name:bar",
})
.getParams().browseParams
.getParams().browseParams,
).toStrictEqual({
filter: "name:bar",
});
@ -55,7 +65,7 @@ describe("authors api .browse() Args Type-safety", () => {
.browse({
filter: "name:bar+slug:-test",
})
.getParams().browseParams
.getParams().browseParams,
).toStrictEqual({
filter: "name:bar+slug:-test",
});
@ -69,14 +79,18 @@ describe("authors api .browse() Args Type-safety", () => {
// @ts-expect-error - order should ony contain field
foo: true,
})
.getOutputFields()
.getOutputFields(),
).toEqual([]);
expect(api.authors.browse().fields({ location: true }).getOutputFields()).toEqual(["location"]);
expect(api.authors.browse().fields({ name: true, website: true }).getOutputFields()).toEqual([
"name",
"website",
]);
expect(
api.authors.browse().fields({ location: true }).getOutputFields(),
).toEqual(["location"]);
expect(
api.authors
.browse()
.fields({ name: true, website: true })
.getOutputFields(),
).toEqual(["name", "website"]);
});
});
@ -84,7 +98,11 @@ describe("authors resource mocked", () => {
let api: TSGhostContentAPI;
beforeEach(() => {
api = new TSGhostContentAPI("https://my-ghost-blog.com", "59d4bf56c73c04a18c867dc3ba", "v5.0");
api = new TSGhostContentAPI(
"https://my-ghost-blog.com",
"59d4bf56c73c04a18c867dc3ba",
"v5.0",
);
fetchMocker.enableMocks();
});
afterEach(() => {
@ -123,7 +141,7 @@ describe("authors resource mocked", () => {
prev: null,
},
},
})
}),
);
const result = await browseQuery.fetch();
expect(fetchMocker).toHaveBeenCalledTimes(1);
@ -134,7 +152,7 @@ describe("authors resource mocked", () => {
"Content-Type": "application/json",
"Accept-Version": "v5.0",
},
}
},
);
expect(result).not.toBeUndefined();
if (result.success) {

View File

@ -4,7 +4,8 @@ import { TSGhostContentAPI } from "@ts-ghost/content-api";
import type { Post } from "./index";
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", () => {
const api = new TSGhostContentAPI(url, key, "v5.0");
@ -27,10 +28,13 @@ describe("posts api .browse() Args Type-safety", () => {
expect(test.getOutputFields()).toEqual(["slug", "title"]);
const fields = ["slug", "title", "foo"] as const;
const unknownOriginFields = fields.reduce((acc, k) => {
const unknownOriginFields = fields.reduce(
(acc, k) => {
acc[k as keyof Post] = true;
return acc;
}, {} as { [k in keyof Post]?: true | undefined });
},
{} as { [k in keyof Post]?: true | undefined },
);
const result = api.posts.browse().fields(unknownOriginFields);
expect(result.getOutputFields()).toEqual(["slug", "title"]);
});
@ -46,10 +50,18 @@ describe("posts api .browse() Args Type-safety", () => {
// @ts-expect-error - shouldnt accept invalid params
expect(() => api.posts.browse({ filter: "slugg:test" })).toThrow();
// @ts-expect-error - shouldnt accept invalid params
expect(() => api.posts.browse({ filter: "slug:test,foo:-[bar,baz]" })).toThrow();
expect(api.posts.browse({ filter: "slug:test,tags:-[bar,baz]" })).toBeDefined();
expect(api.posts.browse({ filter: "slug:test,tags:[bar,baz]" })).toBeDefined();
expect(() =>
api.posts.browse({ filter: "slug:test,foo:-[bar,baz]" }),
).toThrow();
expect(
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();
expect(() =>
api.posts.browse({ filter: "slug:test,food:-[bar,baz]" }),
).toThrow();
});
});

View File

@ -3,7 +3,8 @@ import { beforeEach, describe, expect, test } from "vitest";
import { TSGhostContentAPI } from "@ts-ghost/content-api";
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", () => {
let api: TSGhostContentAPI;
@ -24,7 +25,9 @@ describe("settings integration tests browse", () => {
expect(settings.title).toBe("Astro Starter");
expect(settings.description).toBe("Thoughts, stories and ideas.");
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(
"https://static.ghost.org/v4.0.0/images/publication-cover.jpg",
);
expect(settings.icon).toBeNull();
expect(settings.lang).toBe("en");
expect(settings.timezone).toBe("Etc/UTC");

View File

@ -1,4 +1,4 @@
import { expect, describe, it } from "vitest";
import { describe, expect, it } from "vitest";
import { GhostUserConfigSchema } from "./userconfig";
describe("GhostUserConfigSchema", () => {

View File

@ -18,12 +18,17 @@ export const GhostUserConfigSchema = z.object({
* @default false
*/
disableThemeProvider: z.boolean().optional().default(false),
ThemeProvider: z.object({
ThemeProvider: z
.object({
/** 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 */
disableDefault404: z.boolean().optional().default(false),
/** Allows the user to disable the provided RSS Feed */

View File

@ -1,6 +1,6 @@
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineProject } from 'vitest/config'
import { defineProject } from "vitest/config";
export default defineProject({
test: {
@ -9,4 +9,4 @@ export default defineProject({
watchExclude: [".*\\/node_modules\\/.*", ".*\\/build\\/.*"],
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 UnoCSS from 'unocss/astro';
import { defineConfig } from "astro/config";
import UnoCSS from "unocss/astro";
// https://astro.build/config
export default defineConfig({
site: "https://example.xyz/",
trailingSlash: 'ignore',
trailingSlash: "ignore",
integrations: [
UnoCSS({ injectReset: true }),
ghostcms({
theme: "@matthiesenxyz/astro-ghostcms-brutalbyelian",
ghostURL: "https://ghostdemo.matthiesen.xyz",
})
}),
],
});

View File

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

View File

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

View File

@ -1,21 +1,35 @@
import type { StarlightPlugin, StarlightUserConfig } from '@astrojs/starlight/types'
import type { AstroIntegrationLogger } from 'astro'
import { type StarlightGhostConfig, validateConfig } from './src/schemas/config'
import { vitePluginStarlightGhostConfig } from './src/integrations/vite'
import { facebook, getSettings, invariant, twitter } from './src/utils/api'
import type {
StarlightPlugin,
StarlightUserConfig,
} from "@astrojs/starlight/types";
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 {
const config: StarlightGhostConfig = validateConfig(userConfig)
invariant(settings, "Settings not available... check your api key/url")
export default function starlightGhostCMS(
userConfig?: StarlightGhostConfig,
): StarlightPlugin {
const config: StarlightGhostConfig = validateConfig(userConfig);
invariant(settings, "Settings not available... check your api key/url");
return {
name: '@matthiesenxyz/starlight-ghostcms-plugin',
name: "@matthiesenxyz/starlight-ghostcms-plugin",
hooks: {
setup({ astroConfig, addIntegration, config: starlightConfig, logger, updateConfig: updateStarlightConfig }) {
setup({
astroConfig,
addIntegration,
config: starlightConfig,
logger,
updateConfig: updateStarlightConfig,
}) {
updateStarlightConfig({
social: {
...starlightConfig.social,
@ -25,67 +39,88 @@ export default function starlightGhostCMS(userConfig?: StarlightGhostConfig): St
},
components: {
...starlightConfig.components,
...overrideStarlightComponent(starlightConfig.components, logger, 'MarkdownContent'),
...overrideStarlightComponent(starlightConfig.components, logger, 'Sidebar'),
...overrideStarlightComponent(starlightConfig.components, logger, "SiteTitle"),
}
})
...overrideStarlightComponent(
starlightConfig.components,
logger,
"MarkdownContent",
),
...overrideStarlightComponent(
starlightConfig.components,
logger,
"Sidebar",
),
...overrideStarlightComponent(
starlightConfig.components,
logger,
"SiteTitle",
),
},
});
addIntegration({
name: '@matthiesenxyz/starlight-ghostcms',
name: "@matthiesenxyz/starlight-ghostcms",
hooks: {
'astro:config:setup': ({ injectRoute, updateConfig }) => {
"astro:config:setup": ({ injectRoute, updateConfig }) => {
injectRoute({
pattern: '/blog',
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/index.astro',
pattern: "/blog",
entrypoint:
"@matthiesenxyz/starlight-ghostcms/routes/index.astro",
prerender: true,
})
});
injectRoute({
pattern: '/blog/[slug]',
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/[slug].astro',
pattern: "/blog/[slug]",
entrypoint:
"@matthiesenxyz/starlight-ghostcms/routes/[slug].astro",
prerender: true,
})
});
injectRoute({
pattern: '/blog/about',
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/about.astro',
pattern: "/blog/about",
entrypoint:
"@matthiesenxyz/starlight-ghostcms/routes/about.astro",
prerender: true,
})
});
injectRoute({
pattern: '/blog/authors',
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/authors.astro',
})
pattern: "/blog/authors",
entrypoint:
"@matthiesenxyz/starlight-ghostcms/routes/authors.astro",
});
injectRoute({
pattern: '/rss.xml',
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/rss.xml.ts'
})
pattern: "/rss.xml",
entrypoint:
"@matthiesenxyz/starlight-ghostcms/routes/rss.xml.ts",
});
updateConfig({
vite: {
plugins: [vitePluginStarlightGhostConfig(config)],
},
})
}
}
})
}
});
},
}
},
});
},
},
};
}
function overrideStarlightComponent(
components: StarlightUserConfig['components'],
components: StarlightUserConfig["components"],
logger: AstroIntegrationLogger,
component: keyof NonNullable<StarlightUserConfig['components']>,
component: keyof NonNullable<StarlightUserConfig["components"]>,
) {
if (components?.[component]) {
logger.warn(`It looks like you already have a \`${component}\` component override in your Starlight configuration.`)
logger.warn(`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 ;)")
logger.warn(
`It looks like you already have a \`${component}\` component override in your Starlight configuration.`,
);
logger.warn(
`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 {}
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.
export function vitePluginStarlightGhostConfig(config: StarlightGhostConfig): VitePlugin {
const moduleId = 'virtual:starlight-ghost-config'
const resolvedModuleId = `\0${moduleId}`
const moduleContent = `export default ${JSON.stringify(config)}`
export function vitePluginStarlightGhostConfig(
config: StarlightGhostConfig,
): VitePlugin {
const moduleId = "virtual:starlight-ghost-config";
const resolvedModuleId = `\0${moduleId}`;
const moduleContent = `export default ${JSON.stringify(config)}`;
return {
name: 'vite-plugin-starlight-ghost-config',
name: "vite-plugin-starlight-ghost-config",
load(id) {
return id === resolvedModuleId ? moduleContent : undefined
return id === resolvedModuleId ? moduleContent : undefined;
},
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 { HTMLAttributes } from 'astro/types';
import type { AstroBuiltinAttributes } from "astro";
import type { HTMLAttributes } from "astro/types";
import { z } from "astro/zod";
const linkHTMLAttributesSchema = z.record(
z.union([z.string(), z.number(), z.boolean(), z.undefined()])
) as z.Schema<Omit<HTMLAttributes<'a'>, keyof AstroBuiltinAttributes | 'children'>>;
z.union([z.string(), z.number(), z.boolean(), z.undefined()]),
) as z.Schema<
Omit<HTMLAttributes<"a">, keyof AstroBuiltinAttributes | "children">
>;
export type LinkHTMLAttributes = z.infer<typeof linkHTMLAttributesSchema>;
const badgeSchema = () =>
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(),
});
@ -17,8 +21,8 @@ export const BadgeConfigSchema = () =>
z
.union([z.string(), badgeSchema()])
.transform((badge) => {
if (typeof badge === 'string') {
return { variant: 'default' as const, text: badge };
if (typeof badge === "string") {
return { variant: "default" as const, text: badge };
}
return badge;
})
@ -27,7 +31,7 @@ export const BadgeConfigSchema = () =>
export type Badge = z.output<ReturnType<typeof badgeSchema>>;
export interface Link {
type: 'link';
type: "link";
label: string;
href: string;
isCurrent: boolean;
@ -36,7 +40,7 @@ export interface Link {
}
interface Group {
type: 'group';
type: "group";
label: string;
entries: (Link | Group)[];
collapsed: boolean;

View File

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

View File

@ -1,5 +1,5 @@
import { AstroError } from 'astro/errors'
import { z } from 'astro/zod'
import { AstroError } from "astro/errors";
import { z } from "astro/zod";
const configSchema = z
.object({
@ -14,37 +14,39 @@ const configSchema = z
/**
* The title of the blog.
*/
title: z.string().default('Blog'),
title: z.string().default("Blog"),
/**
* 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"
*/
supportGhost: z.boolean().default(true),
})
.default({})
.default({});
export function validateConfig(userConfig: unknown): StarlightGhostConfig {
const config = configSchema.safeParse(userConfig)
const config = configSchema.safeParse(userConfig);
if (!config.success) {
const errors = config.error.flatten()
const errors = config.error.flatten();
throw new AstroError(
`Invalid starlight-GhostCMS configuration:
${errors.formErrors.map((formError) => ` - ${formError}`).join('\n')}
${errors.formErrors.map((formError) => ` - ${formError}`).join("\n")}
${Object.entries(errors.fieldErrors)
.map(([fieldName, fieldErrors]) => ` - ${fieldName}: ${fieldErrors.join(' - ')}`)
.join('\n')}
.map(
([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",
)
);
}
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
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";
const { CONTENT_API_KEY, CONTENT_API_URL } = loadEnv(
@ -11,14 +11,8 @@ const { CONTENT_API_KEY, CONTENT_API_URL } = loadEnv(
"CONTENT_",
);
invariant(
CONTENT_API_KEY,
"CONTENT_API_KEY Missing from .env"
)
invariant(
CONTENT_API_URL,
"CONTENT_API_URL Missing from .env"
)
invariant(CONTENT_API_KEY, "CONTENT_API_KEY Missing from .env");
invariant(CONTENT_API_URL, "CONTENT_API_URL Missing from .env");
const key: ContentAPICredentials["key"] = CONTENT_API_KEY;
const url: ContentAPICredentials["url"] = CONTENT_API_URL;
@ -79,7 +73,8 @@ export const getSluggedPost = async (slug:string) => {
.include({
authors: true,
tags: true,
}).fetch()
})
.fetch();
if (!results.success) {
throw new Error(results.errors.map((e) => e.message).join(", "));
@ -112,7 +107,8 @@ export const getSluggedPage = async (slug:string) => {
.include({
authors: true,
tags: true,
}).fetch()
})
.fetch();
if (!results.success) {
throw new Error(results.errors.map((e) => e.message).join(", "));

View File

@ -1,21 +1,21 @@
export function isAnyBlogPage(slug: string) {
return slug.match(/^blog(\/?$|\/.+\/?$)/) !== null
return slug.match(/^blog(\/?$|\/.+\/?$)/) !== null;
}
export function isBlogRoot(slug: string) {
return slug === 'blog'
return slug === "blog";
}
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) {
return slug === postSlug
return slug === postSlug;
}
export function isBlogTagsPage(slug: string, tag: string) {
return slug === `blog/tags/${tag}`
return slug === `blog/tags/${tag}`;
}
export function getPageProps(title: string): StarlightPageProps {
@ -23,11 +23,11 @@ export function getPageProps(title: string): StarlightPageProps {
frontmatter: {
title,
},
}
};
}
interface StarlightPageProps {
frontmatter: {
title: string
}
title: string;
};
}

View File

@ -1,5 +1,5 @@
declare module 'virtual:starlight/user-config' {
const Config: import('@astrojs/starlight/types').StarlightConfig
declare module "virtual:starlight/user-config" {
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' {
const StarlightGhostConfig: import('./src/schemas/config').StarlightGhostConfig
declare module "virtual:starlight-ghost-config" {
const StarlightGhostConfig: import("./src/schemas/config").StarlightGhostConfig;
export default StarlightGhostConfig
export default StarlightGhostConfig;
}

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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 = {
docs: defineCollection({ schema: docsSchema() }),