diff --git a/.changeset/config.json b/.changeset/config.json index db35bc78..45a822c3 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["playground","starlight-playground"] + "ignore": ["astro-playground", "starlight-playground"] } diff --git a/.changeset/heavy-maps-act.md b/.changeset/heavy-maps-act.md new file mode 100644 index 00000000..51b68206 --- /dev/null +++ b/.changeset/heavy-maps-act.md @@ -0,0 +1,5 @@ +--- +"@matthiesenxyz/create-astro-ghostcms": minor +--- + +Bumb all templates to new `astro-ghostcms` version v3.3 diff --git a/.changeset/lucky-plants-burn.md b/.changeset/lucky-plants-burn.md new file mode 100644 index 00000000..c2549ee6 --- /dev/null +++ b/.changeset/lucky-plants-burn.md @@ -0,0 +1,13 @@ +--- +"@matthiesenxyz/astro-ghostcms": minor +--- + +This is a HUGE internal update, Our integration is now built on [`Astro-Integration-Kit`](https://github.com/florian-lefebvre/astro-integration-kit) to give better control over the entire `Astro-GhostCMS` Eco-System. + +# Breaking Changes: +- NEW USER CONFIG! Some of the options have changed! Please check the Readme for a current version of the available options! +- Thats it! Some how even though this is almost an entire rebuild, There is no other USER breaking changes aside from the new more advanced config! + +# Updates: +- Moved from `@ts-ghost/core-api` to `@ts-ghost/content-api` as it provides the same functions as the standard core-api but pre-wrapped with a nice `HTTPClientFactory` instead of `HTTPClient`. +- Updated a ton of Dependencies that Dependabot was reporting as needed updated. diff --git a/.changeset/quiet-teachers-knock.md b/.changeset/quiet-teachers-knock.md new file mode 100644 index 00000000..5bec232a --- /dev/null +++ b/.changeset/quiet-teachers-knock.md @@ -0,0 +1,8 @@ +--- +"@matthiesenxyz/starlight-ghostcms": minor +--- + +Bumb GhostCMS API, No user facing breaking changes. + +NEW: +- You can now set a `route: "blog"` in your `astro.config.mjs` to change the default `/` to your blog/posts \ No newline at end of file diff --git a/README.md b/README.md index 92ae3746..43c19781 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ This repo is structured as a `pnpm` monorepo. All of our packages can be found In this Repo you will find the Following: -- `playground`: Development and Testing +- `playgrounds/`: + - [`astro-playground`](./playgrounds/astro-playground/): Playground for Astro-GhostCMS development and testing. + - [`starlight-playground`](./playgrounds/starlight-playground/): Playground of Starlight-GhostCMS development and testing. - `packages/`: - [`create-astro-ghostcms`](./packages/create-astro-ghostcms/): CLI Utility to quickly deploy new Astro-GhostCMS projects. - [`astro-ghostcms`](./packages/astro-ghostcms/): The main Integration! @@ -39,7 +41,6 @@ In this Repo you will find the Following: - [`astro-ghostcms-catppuccin`](./packages/astro-ghostcms-catppuccin/): A dark theme made with Catppuccin and TailwindCSS for Astro-GhostCMS Integration Mode. - [`astro-ghostcms-brutalbyelian`](./packages/astro-ghostcms-brutalbyelian/): [ElianCodes](https://www.elian.codes/) Brutal theme modified to work with Astro-GhostCMS - [`starlight-ghostcms`](./packages/starlight-ghostcms/) A [Starlight Plugin](https://starlight.astro.build/resources/plugins/) to integrate your GhostCMS into your documentation website - - `tsconfig`: *LOCAL* Development package for `@ts-ghost/core-api`. ## Contributing diff --git a/package.json b/package.json index ad52c7b5..b82aa01e 100644 --- a/package.json +++ b/package.json @@ -6,26 +6,26 @@ "node": ">=18.19.0" }, "scripts": { - "dev": "pnpm --filter playground dev", + "astro:dev": "pnpm --filter astro-playground dev", "starlight:dev": "pnpm --filter starlight-playground dev", "lint": "biome check .", "lint:fix": "biome check --apply .", "ci:version": "pnpm changeset version", "ci:publish": "pnpm changeset publish", - "ci:test:api": "pnpm --filter astro-ghostcms test:ci", - "test:api": "pnpm --filter astro-ghostcms test", - "test:api:watch": "pnpm --filter astro-ghostcms test:watch", - "test:api:coverage": "pnpm --filter astro-ghostcms test:coverage", - "test:create": "pnpm --filter create-astro-ghostcms test", - "test:slg": "pnpm --filter starlight-ghostcms test", - "test:slg:watch": "pnpm --filter starlight-ghostcms test:watch", - "test:slg:coverage": "pnpm --filter starlight-ghostcms test:coverage" + "ci:test:integration": "pnpm --filter astro-ghostcms test:ci", + "test:integration": "pnpm --filter astro-ghostcms test", + "test:integration:watch": "pnpm --filter astro-ghostcms test:watch", + "test:integration:coverage": "pnpm --filter astro-ghostcms test:coverage", + "test:create-utility": "pnpm --filter create-astro-ghostcms test", + "test:starlight": "pnpm --filter starlight-ghostcms test", + "test:starlight:watch": "pnpm --filter starlight-ghostcms test:watch", + "test:starlight:coverage": "pnpm --filter starlight-ghostcms test:coverage" }, "devDependencies": { "@biomejs/biome": "1.5.3", "@changesets/cli": "^2.27.1", - "vitest": "^1.3.0", - "vitest-fetch-mock": "^0.2.2", - "@vitest/ui": "1.3.0" + "@vitest/ui": "^1.3.1", + "vitest": "^1.3.1", + "vitest-fetch-mock": "^0.2.2" } } diff --git a/packages/astro-ghostcms/.env.demo b/packages/astro-ghostcms/.env.demo deleted file mode 100644 index e1699c0e..00000000 --- a/packages/astro-ghostcms/.env.demo +++ /dev/null @@ -1,2 +0,0 @@ -CONTENT_API_KEY=a33da3965a3a9fb2c6b3f63b48 -CONTENT_API_URL=https://ghostdemo.matthiesen.xyz \ No newline at end of file diff --git a/packages/astro-ghostcms/CHANGELOG.md b/packages/astro-ghostcms/CHANGELOG.md index a9e053ba..19219270 100644 --- a/packages/astro-ghostcms/CHANGELOG.md +++ b/packages/astro-ghostcms/CHANGELOG.md @@ -60,4 +60,4 @@ - Initialization of changeset cli - Updated dependencies - - @matthiesenxyz/astro-ghostcms-theme-default@0.1.10 + - @matthiesenxyz/astro-ghostcms-theme-default@0.1.10 \ No newline at end of file diff --git a/packages/astro-ghostcms/LICENSE b/packages/astro-ghostcms/LICENSE index 592eb27d..5fe4487a 100644 --- a/packages/astro-ghostcms/LICENSE +++ b/packages/astro-ghostcms/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/packages/astro-ghostcms/README.md b/packages/astro-ghostcms/README.md index 8029250d..cba98a86 100644 --- a/packages/astro-ghostcms/README.md +++ b/packages/astro-ghostcms/README.md @@ -59,7 +59,31 @@ import GhostCMS from '@matthiesenxyz/astro-ghostcms'; // https://astro.build/config export default defineConfig({ site: "https://YOUR-DOMAIN-HERE.com" - integrations: [GhostCMS()], + integrations: [ + GhostCMS({ + // Config Options + ghostURL: "http://example.com", // Recommended to set here, Can also set in .env as CONTENT_API_URL + ThemeProvider: { // Allows you to pass config options to our ThemeProvider if enabled. + disableThemeProvider: false, // OPTIONAL - Default False + theme: "@matthiesenxyz/astro-ghostcms-theme-default", // OPTIONAL - Default Theme shown. + }; + disableDefault404: false, // Allows the user to disable the default `/404 page, to be able to create their own under `/src/pages/404.astro`. + enableRSSFeed: true, // Allows the user to Enable or disable RSS Feed Generation. Default: true + enableOGImages: true, // Allows the user to Enable or disable OG Image Generation. Default: true + verbose: false, // Show the full Log output from All parts of Astro-GhostCMS + Integrations: { + // This allows user config passthrough from Astro-GhostCMS to the Included Integrations + robotsTxt: { + // OPTIONAL + // ADVANCED USAGE - https://www.npmjs.com/package/astro-robots-txt#configuration + }, + sitemap: { + // OPTIONAL + // ADVANCED USAGE - https://docs.astro.build/en/guides/integrations-guide/sitemap + }, + }, + }) + ], }); ``` @@ -113,4 +137,3 @@ For more information and to see the docs please check our website: [Astro-GhostC # Foot Notes & Credits [^1]: Ghost.org, Ghost.io, Ghost are all trademarks of [The Ghost Foundation](https://ghost.org/). This project is Open Source and not directly related to or provided by The Ghost Foundation and is intended to help create a easier method to utilize their provided JavaScript tools to link a Headless GhostCMS install in to your Astro project. - diff --git a/packages/astro-ghostcms/index.ts b/packages/astro-ghostcms/index.ts deleted file mode 100644 index 88715ed3..00000000 --- a/packages/astro-ghostcms/index.ts +++ /dev/null @@ -1,359 +0,0 @@ -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import type { AstroIntegration } from "astro"; -import type { SafeParseError, SafeParseSuccess } from "astro/zod"; -import fse from "fs-extra"; -import { loadEnv } from "vite"; -import ghostRobots from "./src/integrations/robots-txt"; -import ghostSitemap from "./src/integrations/sitemap"; -import { UserConfigSchema } from "./src/schemas"; -import { addVirtualImport } from "./src/utils/add-virtual-import"; -import latestVersion from "./src/utils/latestVersion.js"; -import { fromZodError } from "./src/utils/zod-validation/fromZodError.js"; -import type { UserConfig } from "./types"; - -/** INTERNAL CONSTANTS */ -const IC = { - /** INTERNAL PACKAGE NAME */ - PKG: "@matthiesenxyz/astro-ghostcms", - /** INTERNAL PACKAGE NAME (THEME) */ - DT: "@matthiesenxyz/astro-ghostcms-theme-default", - /** INTERNAL STRING */ - CHECK_ENV: "Checking for Environment Variables...", - /** SET ENV GRABBER MODE */ - MODE: "all", - /** SET ENV GRABBER PREFIXES */ - PREFIXES: "CONTENT_API", - /** INTERNAL STRING */ - KEY_MISSING: "CONTENT_API_KEY Missing from .env/environment variables", - /** INTERNAL STRING */ - URL_MISSING: - "CONTENT_API_URL Missing from .env/environment variables or ghostURL under the integration settings in `astro.config.mjs`", - /** INTERNAL STRING */ - IT: "Injecting Theme: ", - /** INTERNAL STRING */ - IDR: "Injecting Default Routes...", - /** INTERNAL STRING */ - ITR: "Injecting Default Theme Routes...", - /** INTERNAL STRING */ - IRD: "Route Injection Disabled - Skipping...", - /** INTERNAL STRING */ - IIR: "Injecting Integration Route: ", - /** INTERNAL STRING */ - II: "Injecting Integration: ", - /** INTERNAL STRING */ - AIbU: "Already Imported by User: ", - /** INTERNAL STRING */ - CF: "Checking for ", - /** INTERNAL STRING */ - CONFSETUPDONE: "Step Complete", - /** INTERNAL STRING */ - F0FR: "Inject `/404` Route", - /** INTERNAL STRING */ - RSS: "Injecting `/rss.xml` Route and `@astrojs/rss` Integration", - /** INTERNAL STRING */ - NOURL: - "No Ghost URL defined in User Config: Falling back to environment variables.", - /** INTERNAL STRING */ - id404: "404 Injection Disabled", - /** INTERNAL STRING */ - idRSS: "RSS Injection Disabled", - /** INTERNAL STRING */ - satori_e: "Injecting Satori-OpenGraph Generator", - /** INTERNAL STRING */ - satori_d: "Satori Injection disabled", -}; - -/** CONTENT API ENVIRONMENT VARIABLES */ -const ENV = loadEnv(IC.MODE, process.cwd(), IC.PREFIXES); - -/** Astro-GhostCMS Integration - * @ For more information and to see the docs check - * @see https://astro-ghostcms.xyz - */ -export default function GhostCMS(options: UserConfig): AstroIntegration { - return { - name: "astro-ghostcms", - hooks: { - "astro:config:setup": async ({ - injectRoute, - config, - updateConfig, - logger, - }) => { - // DEFINE LOGGERS - const logConfigCheck = logger.fork("astro-ghostcms/config:check"); - const logConfigSetup = logger.fork("astro-ghostcms/config:setup"); - - // CHECK USER CONFIG AND MAKE AVAILBLE TO INTEGRATIONS - logConfigCheck.info("Checking Config..."); - const GhostUserConfig = UserConfigSchema.safeParse( - options || {}, - ) as SafeParseSuccess; - if (!GhostUserConfig.success) { - const validationError = fromZodError( - (GhostUserConfig as unknown as SafeParseError).error, - ); - logConfigCheck.error(`Config Error - ${validationError}`); - throw Error(""); - } - const GhostConfig = GhostUserConfig.data; - const GCD = { - theme: GhostConfig.theme, - dRI: GhostConfig.disableRouteInjection, - dCO: GhostConfig.disableConsoleOutput, - SM: GhostConfig.sitemap, - RTXT: GhostConfig.robotstxt, - gSite: GhostConfig.ghostURL, - dRSS: GhostConfig.disableRSS, - d404: GhostConfig.disable404, - dOG: GhostConfig.disableSatoriOG, - }; - - // Check For ENV Variables - if (!GCD.dCO) { - logConfigCheck.info(IC.CHECK_ENV); - } - if (ENV.CONTENT_API_KEY === undefined) { - logConfigCheck.error(IC.KEY_MISSING); - throw IC.KEY_MISSING; - } - if (GCD.gSite === undefined) { - logConfigCheck.warn(IC.NOURL); - if (ENV.CONTENT_API_URL === undefined) { - logConfigCheck.error(IC.URL_MISSING); - throw IC.URL_MISSING; - } - } - - if (!GCD.dRI) { - // THEME SELECTOR - if (GCD.theme === IC.DT) { - if (!GCD.dCO) { - logConfigCheck.info(IC.IT + IC.DT); - } - } else { - if (!GCD.dCO) { - logConfigCheck.info(IC.IT + GCD.theme); - } - } - // INJECT ROUTES - //// DEFAULT PROGRAM ROUTES - if (!GCD.dCO) { - logConfigSetup.info(IC.IDR); - } - if (!GCD.d404) { - if (!GCD.dCO) { - logConfigSetup.info(IC.F0FR); - } - injectRoute({ - pattern: "/404", - entrypoint: `${IC.PKG}/404.astro`, - }); - } else { - if (!GCD.dCO) { - logConfigSetup.info(IC.id404); - } - } - - if (!GCD.dRSS) { - if (!GCD.dCO) { - logConfigSetup.info(IC.RSS); - } - injectRoute({ - pattern: "/rss-style.xsl", - entrypoint: `${IC.PKG}/rss-style.xsl.ts`, - }); - injectRoute({ - pattern: "/rss.xml", - entrypoint: `${IC.PKG}/rss.xml.ts`, - }); - } else { - if (!GCD.dCO) { - logConfigSetup.info(IC.idRSS); - } - } - - if (!GCD.dOG) { - if (!GCD.dCO) { - logConfigSetup.info(IC.satori_e); - } - injectRoute({ - pattern: "/open-graph/[slug].png", - entrypoint: `${IC.PKG}/open-graph/[slug].png.ts`, - }); - injectRoute({ - pattern: "/open-graph/index.png", - entrypoint: `${IC.PKG}/open-graph/index.png.ts`, - }); - injectRoute({ - pattern: "/open-graph/authors.png", - entrypoint: `${IC.PKG}/open-graph/authors.png.ts`, - }); - injectRoute({ - pattern: "/open-graph/author/[slug].png", - entrypoint: `${IC.PKG}/open-graph/author/[slug].png.ts`, - }); - injectRoute({ - pattern: "/open-graph/tags.png", - entrypoint: `${IC.PKG}/open-graph/tags.png.ts`, - }); - injectRoute({ - pattern: "/open-graph/tag/[slug].png", - entrypoint: `${IC.PKG}/open-graph/tag/[slug].png.ts`, - }); - } else { - if (!GCD.dCO) { - logConfigSetup.info(IC.satori_d); - } - } - - // THEME ROUTES - if (!GCD.dCO) { - logConfigSetup.info(IC.ITR); - } - - injectRoute({ - pattern: "/", - entrypoint: `${GCD.theme}/index.astro`, - }); - injectRoute({ - pattern: "/[slug]", - entrypoint: `${GCD.theme}/[slug].astro`, - }); - injectRoute({ - pattern: "/tags", - entrypoint: `${GCD.theme}/tags.astro`, - }); - injectRoute({ - pattern: "/authors", - entrypoint: `${GCD.theme}/authors.astro`, - }); - injectRoute({ - pattern: "/tag/[slug]", - entrypoint: `${GCD.theme}/tag/[slug].astro`, - }); - injectRoute({ - pattern: "/author/[slug]", - entrypoint: `${GCD.theme}/author/[slug].astro`, - }); - injectRoute({ - pattern: "/archives/[...page]", - entrypoint: `${GCD.theme}/archives/[...page].astro`, - }); - } else { - if (!GCD.dCO) { - logConfigSetup.info(IC.IRD); - } - } - - // IMPORT INTEGRATIONS & INTEGRATION ROUTES - const integrations = [...config.integrations]; - - // IMPORT INTEGRATION: @ASTROJS/SITEMAP - if (!GCD.dCO) { - logConfigSetup.info(`${IC.CF}@astrojs/sitemap`); - } - if (!integrations.find(({ name }) => name === "@astrojs/sitemap")) { - if (!GCD.dCO) { - logConfigSetup.info(`${IC.II}@astrojs/sitemap`); - } - integrations.push(ghostSitemap(GCD.SM)); - } else { - if (!GCD.dCO) { - logConfigSetup.info(`${IC.AIbU}@astrojs/sitemap`); - } - } - - // IMPORT INTEGRATION: ASTRO-ROBOTS-TXT - if (!GCD.dCO) { - logConfigSetup.info(`${IC.CF}astro-robots-txt`); - } - if (!integrations.find(({ name }) => name === "astro-robots-txt")) { - if (!GCD.dCO) { - logConfigSetup.info(`${IC.II}astro-robots-txt`); - } - integrations.push(ghostRobots(GCD.RTXT)); - } else { - if (!GCD.dCO) { - logConfigSetup.info(`${IC.AIbU}astro-robots-txt`); - } - } - - // FINAL STEP TO KEEP INTEGRATION LIVE - try { - updateConfig({ - // UPDATE ASTRO CONFIG WITH INTEGRATED INTEGRATIONS - integrations: [ghostSitemap(GCD.SM), ghostRobots(GCD.RTXT)], - vite: { - optimizeDeps: { exclude: ["@resvg/resvg-js"] }, - }, - }); - } catch (e) { - logConfigSetup.error(e as string); - throw e; - } - - addVirtualImport({ - name: "virtual:@matthiesenxyz/astro-ghostcms/config", - content: `export default ${JSON.stringify(GhostUserConfig.data)}`, - updateConfig, - }); - }, - "astro:config:done": async ({ logger }) => { - // Config Done - const logConfigDone = logger.fork("astro-ghostcms/config:done"); - const pJSON = await fse.readJson( - path.resolve(fileURLToPath(import.meta.url), "..", "package.json"), - ); - const pkgVer = pJSON.version; - logConfigDone.info(`Config Done. Current Version: v${pkgVer}`); - }, - "astro:server:setup": async ({ logger }) => { - // Dev Server Start - const logServerSetup = logger.fork("astro-ghostcms/server:setup"); - const logCurrentVersion = logger.fork("astro-ghostcms/current-version"); - const logNpmVersion = logger.fork("astro-ghostcms/npm-pub-version"); - const logCheck = logger.fork("astro-ghostcms/check"); - const pJSON = await fse.readJson( - path.resolve(fileURLToPath(import.meta.url), "..", "package.json"), - ); - const pkgVer = pJSON.version; - const npmVER = await latestVersion(IC.PKG); - if (pkgVer !== npmVER) { - logCurrentVersion.warn(`Current Installed Version is v${pkgVer}`); - logNpmVersion.warn(`Latest Published Version is v${npmVER}`); - logCheck.warn("Please consider updating."); - } - logServerSetup.info( - "Setting up Astro-GhostCMS server for Development!", - ); - }, - "astro:server:start": async ({ logger }) => { - // Server Start - const logServerStart = logger.fork("astro-ghostcms/server:start"); - logServerStart.info("Astro-GhostCMS Integration Ready!"); - }, - "astro:build:done": async ({ logger }) => { - // Build Done - const logBuildDone = logger.fork("astro-ghostcms/build:done"); - const logCurrentVersion = logger.fork("astro-ghostcms/current-version"); - const logNpmVersion = logger.fork("astro-ghostcms/npm-pub-version"); - const logCheck = logger.fork("astro-ghostcms/check"); - const pJSON = await fse.readJson( - path.resolve(fileURLToPath(import.meta.url), "..", "package.json"), - ); - const pkgVer = pJSON.version; - const npmVER = await latestVersion(IC.PKG); - if (pkgVer !== npmVER) { - logCurrentVersion.warn(`Current Installed Version is v${pkgVer}`); - logNpmVersion.warn(`Latest Published Version is v${npmVER}`); - logCheck.warn("Please consider updating."); - } - logBuildDone.info( - `Build Complete, Integration Now ready for Production. Astro-GhostCMS v${pkgVer}`, - ); - }, - }, - }; -} diff --git a/packages/astro-ghostcms/package.json b/packages/astro-ghostcms/package.json index 036503d0..30d210f9 100644 --- a/packages/astro-ghostcms/package.json +++ b/packages/astro-ghostcms/package.json @@ -1,111 +1,85 @@ { - "name": "@matthiesenxyz/astro-ghostcms", - "description": "Astro GhostCMS integration to allow easier importing of GhostCMS Content", - "version": "3.2.9", - "homepage": "https://astro-ghostcms.xyz/", - "type": "module", - "license": "MIT", - "publishConfig": { - "access": "public" - }, - "sideEffects": false, - "author": { - "email": "adam@matthiesen.xyz", - "name": "Adam Matthiesen - MatthiesenXYZ", - "url": "https://matthiesen.xyz" - }, - "keywords": [ - "astro-component", - "astro-integration", - "withastro", - "astro", - "blog", - "content", - "integration", - "ghost", - "ghostcms", - "ghostcms-theme", - "ghost-theme", - "astro-theme" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/MatthiesenXYZ/astro-ghostcms.git" - }, - "bugs": { - "url": "https://github.com/MatthiesenXYZ/astro-ghostcms/issues", - "email": "issues@astro-ghostcms.xyz" - }, - "main": "index.ts", - "types": "types.d.ts", - "files": [ - "src", - ".env.demo", - "index.ts", - "tsconfig.json", - "types.d.ts" - ], - "exports": { - ".": "./index.ts", - "./api": { - "types": "./src/api/index.ts", - "default": "./src/api/index.ts" + "name": "@matthiesenxyz/astro-ghostcms", + "description": "Astro GhostCMS integration to allow easier importing of GhostCMS Content", + "version": "3.2.9", + "homepage": "https://astro-ghostcms.xyz/", + "type": "module", + "license": "MIT", + "publishConfig": { + "access": "public" }, - "./api-core": "./src/api/content-api/index.ts", - "./config": { - "types": "./src/integrations/virtual.d.ts", - "default": "./src/integrations/virtual-config.ts" + "sideEffects": false, + "author": { + "email": "adam@matthiesen.xyz", + "name": "Adam Matthiesen - MatthiesenXYZ", + "url": "https://matthiesen.xyz" }, - "./satoriOG": "./src/integrations/satori.ts", - "./404.astro": "./src/default-routes/404/404.astro", - "./rss.xml.ts": "./src/default-routes/rss.xml.ts", - "./rss-style.xsl.ts": "./src/default-routes/rss-style.xsl.ts", - "./open-graph/index.png.ts": "./src/default-routes/open-graph/index.png.ts", - "./open-graph/authors.png.ts": "./src/default-routes/open-graph/authors.png.ts", - "./open-graph/tags.png.ts": "./src/default-routes/open-graph/tags.png.ts", - "./open-graph/[slug].png.ts": "./src/default-routes/open-graph/[slug].png.ts", - "./open-graph/author/[slug].png.ts": "./src/default-routes/open-graph/author/[slug].png.ts", - "./open-graph/tag/[slug].png.ts": "./src/default-routes/open-graph/tag/[slug].png.ts" - }, - "scripts": { - "test": "vitest run", - "test:watch": "vitest", - "test:coverage": "vitest run --coverage", - "test:ci": "vitest run --coverage.enabled --coverage.reporter='text-summary'" - }, - "devDependencies": { - "@astrojs/check": "^0.5.4", - "@ts-ghost/core-api": "*", - "@ts-ghost/tsconfig": "*", - "@matthiesenxyz/astro-ghostcms-theme-default": "*", - "@matthiesenxyz/astro-ghostcms-catppuccin": "*", - "@types/fs-extra": "^11.0.1", - "@types/node": "^20.11.19", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-astro": "^0.31.4", - "prettier": "^3.2.5", - "prettier-plugin-astro": "^0.13.0", - "typescript": "^5.3.3", - "vitest": "^1.3.0", - "vitest-fetch-mock": "^0.2.2" - }, - "dependencies": { - "@matthiesenxyz/astro-ghostcms-theme-default": "^0.1.13", - "@astrojs/rss": "^4.0.5", - "@astrojs/sitemap": "^3.0.5", - "@resvg/resvg-js": "^2.6.0", - "@ts-ghost/core-api": "^5.1.2", - "astro": "^4.4.0", - "astro-robots-txt": "^1.0.0", - "fs-extra": "^11.1.0", - "package-json": "9.0.0", - "satori": "^0.10.11", - "satori-html": "^0.3.2", - "vite": "^5.1.3", - "vite-tsconfig-paths": "^4.2.2", - "zod": "^3.22.4" + "keywords": [ + "astro-component", + "astro-integration", + "withastro", + "astro", + "blog", + "content", + "integration", + "ghost", + "ghostcms", + "ghostcms-theme", + "ghost-theme", + "astro-theme" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/MatthiesenXYZ/astro-ghostcms.git" + }, + "bugs": { + "url": "https://github.com/MatthiesenXYZ/astro-ghostcms/issues", + "email": "issues@astro-ghostcms.xyz" + }, + "files": [ + "src", + "CHANGELOG.md", + "LICENSE", + "README.md" + ], + "exports": { + ".": "./src/index.ts", + "./api": "./src/api/index.ts", + "./satoriOG": "./src/integrations/satoriog/satori.ts" + }, + "scripts": { + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "test:ci": "vitest run --coverage.enabled --coverage.reporter='text-summary'" + }, + "enginesStrict": { + "node": ">=18.19.0" + }, + "peerDependencies": { + "astro": ">=4.4.1" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "@types/node": "^20.11.24", + "vitest": "^1.3.1", + "vitest-fetch-mock": "^0.2.2" + }, + "dependencies": { + "@astrojs/rss": "^4.0.5", + "@astrojs/sitemap": "^3.1.1", + "@matthiesenxyz/astro-ghostcms-theme-default": "^0.1.13", + "@resvg/resvg-js": "^2.6.0", + "@ts-ghost/core-api": "^6.0.0", + "@ts-ghost/content-api": "^4.0.12", + "astro-integration-kit": "^0.5.1", + "astro-robots-txt": "^1.0.0", + "fs-extra": "^11.2.0", + "package-json": "^10.0.0", + "picocolors": "^1.0.0", + "satori": "^0.10.13", + "satori-html": "^0.3.2", + "vite": "^5.1.4" + } } -} + \ No newline at end of file diff --git a/packages/astro-ghostcms/src/api/content-api/content-api.ts b/packages/astro-ghostcms/src/api/content-api/content-api.ts deleted file mode 100644 index 6035e96a..00000000 --- a/packages/astro-ghostcms/src/api/content-api/content-api.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { - APIComposer, - BasicFetcher, - HTTPClient, - contentAPICredentialsSchema, - slugOrIdSchema, -} from "@ts-ghost/core-api"; - -import { - authorsIncludeSchema, - authorsSchema, - pagesIncludeSchema, - pagesSchema, - postsIncludeSchema, - postsSchema, - settingsSchema, - tagsIncludeSchema, - tagsSchema, - tiersIncludeSchema, - tiersSchema, -} from "./schemas"; - -export type { ContentAPICredentials, APIVersions } from "@ts-ghost/core-api"; - -export enum BrowseEndpointType { - authors = "authors", - tiers = "tiers", - posts = "posts", - pages = "pages", - tags = "tags", - settings = "settings", -} - -export default class TS_API { - private httpClient: HTTPClient; - - constructor( - protected readonly url: string, - protected readonly key: string, - protected readonly version: Version, - ) { - const apiCredentials = contentAPICredentialsSchema.parse({ - key, - version, - url, - }); - this.httpClient = new HTTPClient({ - ...apiCredentials, - endpoint: "content", - }); - } - - get authors() { - return new APIComposer( - "authors", - { - schema: authorsSchema, - identitySchema: slugOrIdSchema, - include: authorsIncludeSchema, - }, - this.httpClient, - ).access(["read", "browse"]); - } - get tiers() { - return new APIComposer( - "tiers", - { - schema: tiersSchema, - identitySchema: slugOrIdSchema, - include: tiersIncludeSchema, - }, - this.httpClient, - ).access(["browse", "read"]); - } - get posts() { - return new APIComposer( - "posts", - { - schema: postsSchema, - identitySchema: slugOrIdSchema, - include: postsIncludeSchema, - }, - this.httpClient, - ).access(["browse", "read"]); - } - get pages() { - return new APIComposer( - "pages", - { - schema: pagesSchema, - identitySchema: slugOrIdSchema, - include: pagesIncludeSchema, - }, - this.httpClient, - ).access(["browse", "read"]); - } - get tags() { - return new APIComposer( - "tags", - { - schema: tagsSchema, - identitySchema: slugOrIdSchema, - include: tagsIncludeSchema, - }, - this.httpClient, - ).access(["browse", "read"]); - } - - get settings() { - return new BasicFetcher( - "settings", - { output: settingsSchema }, - this.httpClient, - ); - } -} diff --git a/packages/astro-ghostcms/src/api/content-api/index.ts b/packages/astro-ghostcms/src/api/content-api/index.ts deleted file mode 100644 index fb09f087..00000000 --- a/packages/astro-ghostcms/src/api/content-api/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { default as TS_API } from "./content-api"; -export * from "./schemas"; - -export type { - InferFetcherDataShape, - InferResponseDataShape, - BrowseParams, -} from "@ts-ghost/core-api"; diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/authors/index.ts b/packages/astro-ghostcms/src/api/content-api/schemas/authors/index.ts deleted file mode 100644 index f9adb07f..00000000 --- a/packages/astro-ghostcms/src/api/content-api/schemas/authors/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./authors"; diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/helpers/index.ts b/packages/astro-ghostcms/src/api/content-api/schemas/helpers/index.ts deleted file mode 100644 index f4fb9f52..00000000 --- a/packages/astro-ghostcms/src/api/content-api/schemas/helpers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./socials"; diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/pages/index.ts b/packages/astro-ghostcms/src/api/content-api/schemas/pages/index.ts deleted file mode 100644 index c4e34b27..00000000 --- a/packages/astro-ghostcms/src/api/content-api/schemas/pages/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./pages"; diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/posts/index.ts b/packages/astro-ghostcms/src/api/content-api/schemas/posts/index.ts deleted file mode 100644 index 4f89127a..00000000 --- a/packages/astro-ghostcms/src/api/content-api/schemas/posts/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./posts"; diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/settings/index.ts b/packages/astro-ghostcms/src/api/content-api/schemas/settings/index.ts deleted file mode 100644 index dcf101b0..00000000 --- a/packages/astro-ghostcms/src/api/content-api/schemas/settings/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./settings"; diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/tags/index.ts b/packages/astro-ghostcms/src/api/content-api/schemas/tags/index.ts deleted file mode 100644 index 006f4bf5..00000000 --- a/packages/astro-ghostcms/src/api/content-api/schemas/tags/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./tags"; diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/tiers/index.ts b/packages/astro-ghostcms/src/api/content-api/schemas/tiers/index.ts deleted file mode 100644 index aec8c265..00000000 --- a/packages/astro-ghostcms/src/api/content-api/schemas/tiers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./tiers"; diff --git a/packages/starlight-ghostcms/src/utils/api/content-api/content-api.test.ts b/packages/astro-ghostcms/src/api/ghostAPI.test.ts similarity index 82% rename from packages/starlight-ghostcms/src/utils/api/content-api/content-api.test.ts rename to packages/astro-ghostcms/src/api/ghostAPI.test.ts index e47ec835..a7b190f8 100644 --- a/packages/starlight-ghostcms/src/utils/api/content-api/content-api.test.ts +++ b/packages/astro-ghostcms/src/api/ghostAPI.test.ts @@ -1,11 +1,15 @@ import { assert, beforeEach, describe, expect, test } from "vitest"; -import TS_API from "./content-api"; +import { TSGhostContentAPI } from "@ts-ghost/content-api"; describe("content-api", () => { - let api: TS_API; + let api: TSGhostContentAPI; beforeEach(() => { - api = new TS_API("https://ghost.org", "59d4bf56c73c04a18c867dc3ba", "v5.0"); + api = new TSGhostContentAPI( + "https://ghost.org", + "59d4bf56c73c04a18c867dc3ba", + "v5.0", + ); }); test("content-api", () => { @@ -14,21 +18,25 @@ describe("content-api", () => { test("content-api shouldn't instantiate with an incorrect url", () => { assert.throws(() => { - const api = new TS_API("ghost.org", "59d4bf56c73c04a18c867dc3ba", "v5.0"); + const api = new TSGhostContentAPI( + "ghost.org", + "59d4bf56c73c04a18c867dc3ba", + "v5.0", + ); api.settings; }); }); test("content-api shouldn't instantiate with an incorrect key", () => { assert.throws(() => { - const api = new TS_API("https://ghost.org", "a", "v5.0"); + const api = new TSGhostContentAPI("https://ghost.org", "a", "v5.0"); api.settings; }); }); test("content-api shouldn't instantiate with an incorrect version", () => { assert.throws(() => { - const api = new TS_API( + const api = new TSGhostContentAPI( "https://ghost.org", "1efedd9db174adee2d23d982:4b74dca0219bad629852191af326a45037346c2231240e0f7aec1f9371cc14e8", // @ts-expect-error diff --git a/packages/astro-ghostcms/src/api/api-functions.ts b/packages/astro-ghostcms/src/api/ghostAPI.ts similarity index 86% rename from packages/astro-ghostcms/src/api/api-functions.ts rename to packages/astro-ghostcms/src/api/ghostAPI.ts index dde659e8..ce46cecc 100644 --- a/packages/astro-ghostcms/src/api/api-functions.ts +++ b/packages/astro-ghostcms/src/api/ghostAPI.ts @@ -1,23 +1,24 @@ -import { TS_API } from "./content-api"; -import type { Page, Post } from "./content-api/schemas"; +import { TSGhostContentAPI } from "@ts-ghost/content-api"; +import type { Page, Post } from "../schemas/api"; // LOAD ENVIRONMENT VARIABLES import { loadEnv } from "vite"; -import config from "../integrations/virtual-config"; - -const CONF_URL = config.ghostURL; - const { CONTENT_API_KEY, CONTENT_API_URL } = loadEnv( "all", process.cwd(), "CONTENT_", ); -const ghostApiKey = CONTENT_API_KEY; -const ghostUrl = CONF_URL ? CONF_URL : CONTENT_API_URL; +// LOAD CONFIG +import config from "virtual:@matthiesenxyz/astro-ghostcms/config"; +const CONF_URL = config.ghostURL; + +// SETUP GHOST API +const ghostApiKey = CONTENT_API_KEY || ""; +const ghostUrl = CONF_URL || CONTENT_API_URL || ""; const version = "v5.0"; -const api = new TS_API(ghostUrl, ghostApiKey, version); +const api = new TSGhostContentAPI(ghostUrl, ghostApiKey, version); export const getAllAuthors = async () => { const results = await api.authors diff --git a/packages/astro-ghostcms/src/api/index.ts b/packages/astro-ghostcms/src/api/index.ts index 91557560..595a15a8 100644 --- a/packages/astro-ghostcms/src/api/index.ts +++ b/packages/astro-ghostcms/src/api/index.ts @@ -1,3 +1,3 @@ -export * from "./api-functions"; -export * from "./content-api/schemas"; +export * from "./ghostAPI"; export * from "./invariant"; +export * from "../schemas/api/index"; diff --git a/packages/astro-ghostcms/src/api/invariant.ts b/packages/astro-ghostcms/src/api/invariant.ts index 787f7935..8d37a1ba 100644 --- a/packages/astro-ghostcms/src/api/invariant.ts +++ b/packages/astro-ghostcms/src/api/invariant.ts @@ -20,7 +20,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const tinyinvariant = "merged"; const isProduction: boolean = process.env.NODE_ENV === "production"; const prefix: string = "Invariant failed"; diff --git a/packages/astro-ghostcms/src/astro-ghostcms.ts b/packages/astro-ghostcms/src/astro-ghostcms.ts new file mode 100644 index 00000000..21c401ab --- /dev/null +++ b/packages/astro-ghostcms/src/astro-ghostcms.ts @@ -0,0 +1,303 @@ +import fse from "fs-extra"; +import { createResolver, defineIntegration } from "astro-integration-kit"; +import { corePlugins } from "astro-integration-kit/plugins"; +import { AstroError } from "astro/errors"; +import type { AstroIntegration } from "astro"; +import c from "picocolors"; +import { loadEnv } from "vite"; +import sitemap from "@astrojs/sitemap"; +import robotsTxt from "astro-robots-txt"; + +// Internal Imports +import { GhostUserConfigSchema } from "./schemas/userconfig"; +import ghostRSS from "./integrations/rssfeed"; +import ghostOGImages from "./integrations/satoriog"; +import ghostThemeProvider from "./integrations/themeprovider"; +import latestVersion from "./utils/latestVersion"; + +// Load environment variables +const ENV = loadEnv("all", process.cwd(), "CONTENT_API"); + +/** Astro-GhostCMS Integration + * @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! + */ +export default defineIntegration({ + name: "@matthiesenxyz/astro-ghostcms", + optionsSchema: GhostUserConfigSchema, + plugins: [...corePlugins], + setup({ options, name }) { + const { resolve } = createResolver(import.meta.url); + + return { + "astro:config:setup": ({ + watchIntegration, + hasIntegration, + addIntegration, + addVirtualImports, + injectRoute, + logger, + }) => { + // Set up verbose logging + const verbose = options.verbose; + + // Configure Loggers + const GhostLogger = logger.fork(c.bold(c.blue("👻 Astro-GhostCMS"))); + + const loggerTagged = (message: string) => { + return logger.fork(`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue(message)}`) + } + + // Configure ENV Logger + const GhostENVLogger = loggerTagged("ENV Check"); + + // Configure Integration Loggers & verbose logging + const GhostIntegrationLogger = loggerTagged("Integrations"); + + // Configure Route Logger & verbose logging + const GhostRouteLogger = loggerTagged("Router"); + + // Log Info Helper + const intLogInfo = (message:string) => { + if (verbose) { + GhostIntegrationLogger.info(message); + } + }; + + // Log Route Info Helper + const routeLogInfo = (message:string) => { + if (verbose) { + GhostRouteLogger.info(message); + } + }; + + // Local Integration Helper + const localIntegration = (enabled: boolean, name: string, integration: AstroIntegration) => { + if (enabled) { + addIntegration(integration); + } else { + intLogInfo(c.gray(`${name} integration is disabled`)); + } + } + + // Check External Integration Helper + const checkIntegration = (name: string, integration: AstroIntegration) => { + if (!hasIntegration(name)) { + intLogInfo(c.bold(c.magenta(`Adding ${c.blue(name)} integration`))); + addIntegration(integration); + } else { + intLogInfo(c.gray(`${name} integration already exists, skipping...`)); + } + } + + // Inject Route Helper + const routeHelper = (routename: string, enabled: boolean, pattern: string, entrypoint: string) => { + if (enabled) { + routeLogInfo(c.bold(c.cyan(`Setting up ${routename} route`))); + injectRoute({ + pattern: pattern, + entrypoint: resolve(`./routes${entrypoint}`), + prerender: true, + }); + } else { + routeLogInfo(c.gray(`${routename} route is disabled, Skipping...`)); + } + } + + // Setup Watch Integration for Hot Reload during DEV + watchIntegration(resolve()); + GhostLogger.info("Initializing @matthiesenxyz/astro-ghostcms..."); + + + // Check for GhostCMS environment variables + GhostENVLogger.info( + c.bold( + c.yellow( + "Checking for GhostCMS environment variables & user configuration", + ), + ), + ); + + // Check for GhostCMS API Key + 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`, + ); + } + + // Check for GhostCMS URL + if (options.ghostURL === undefined) { + 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.info( + c.bold(c.green("GhostCMS environment variables are set")), + ); + + // Set up Astro-GhostCMS Integrations + GhostIntegrationLogger.info( + c.bold(c.magenta("Configuring Enabled Integrations")), + ); + + // Setup GhostCMS Theme Provider + localIntegration( + !options.ThemeProvider.disableThemeProvider, + "Theme Provider", + ghostThemeProvider({ + theme: options.ThemeProvider.theme, + verbose, + }) + ); + + // Setup GhostCMS OG Image Provider + localIntegration( + options.enableOGImages, + "Satori OG Images", + ghostOGImages({ verbose }) + ); + + // Setup GhostCMS RSS Feed Provider + localIntegration( + options.enableRSSFeed, + "RSS Feed", + ghostRSS({ verbose }) + ); + + // Setup @astrojs/sitemap Integration + checkIntegration( + "@astrojs/sitemap", + sitemap(options.Integrations.sitemap) + ); + + // Setup astro-robots-txt Integration + checkIntegration( + "astro-robots-txt", + robotsTxt(options.Integrations.robotsTxt) + ); + + // Setup Default 404 Page + routeHelper( + "Default 404 Page", + !options.disableDefault404, + "/404", + "/404.astro" + ); + + // Add virtual imports for user configuration + addVirtualImports({ + "virtual:@matthiesenxyz/astro-ghostcms/config": `export default ${JSON.stringify( + options, + )}`, + }); + + }, + "astro:config:done": ({ logger }) => { + // Configure Loggers + const GhostLogger = logger.fork( + `${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.green( + "CONFIG", + )}`, + ); + + // Log Configuration Complete + GhostLogger.info( + c.bold(c.green("Integration Setup & Configuration Complete")), + ); + }, + "astro:server:start": async ({ logger }) => { + const loggerTagged = (message: string) => { + return logger.fork(`${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.green(message)}`) + } + // Configure Loggers + const GhostLogger = loggerTagged("DEV"); + + const GhostUpdateLogger = loggerTagged("VERSION CHECK"); + + // Start the DEV server + GhostLogger.info( + c.bold(c.magenta("Running Astro-GhostCMS in Deveopment mode 🚀")), + ); + + // Check for updates + + // Get the latest version of Astro-GhostCMS + const currentNPMVersion = await latestVersion( + "@matthiesenxyz/astro-ghostcms", + ); + + // Get the local version of Astro-GhostCMS + const packageJson = await fse.readJson(resolve("../package.json")); + const localVersion = packageJson.version; + + // Log the version check + 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`, + ); + } else { + GhostUpdateLogger.info( + c.bold(c.green(`Astro-GhostCMS is up to date! v${localVersion}`)), + ); + } + }, + "astro:build:done": ({ logger }) => { + // Configure Loggers + const GhostLogger = logger.fork( + `${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.bold( + c.green("BUILD"), + )}`, + ); + + // Log Build Complete + GhostLogger.info( + c.bold(c.magenta("Running Astro-GhostCMS in Production mode 🚀")), + ); + }, + }; + }, +}); diff --git a/packages/astro-ghostcms/src/index.ts b/packages/astro-ghostcms/src/index.ts new file mode 100644 index 00000000..0bcf5e48 --- /dev/null +++ b/packages/astro-ghostcms/src/index.ts @@ -0,0 +1,3 @@ +import astroGhostCMS from "./astro-ghostcms"; + +export default astroGhostCMS; diff --git a/packages/astro-ghostcms/src/integrations/robots-txt.ts b/packages/astro-ghostcms/src/integrations/robots-txt.ts deleted file mode 100644 index 38cbe70c..00000000 --- a/packages/astro-ghostcms/src/integrations/robots-txt.ts +++ /dev/null @@ -1,28 +0,0 @@ -import robotsTxt, { type RobotsTxtOptions } from "astro-robots-txt"; -import type { UserConfig } from "../schemas"; - -export function getRobotsTxtConfig( - opts: UserConfig["robotstxt"], -): RobotsTxtOptions { - const robotsConfig: RobotsTxtOptions = {}; - if (opts?.host) { - robotsConfig.host = opts.host; - } - if (opts?.policy) { - robotsConfig.policy = opts.policy; - } - if (opts?.sitemap) { - robotsConfig.sitemap = opts.sitemap; - } - if (opts?.sitemapBaseFileName) { - robotsConfig.sitemapBaseFileName = opts.sitemapBaseFileName; - } - return robotsConfig; -} - -/** - * A wrapped version of the `astro-robots-txt` integration for GhostCMS. - */ -export default function ghostRobots(opts: UserConfig["robotstxt"]) { - return robotsTxt(getRobotsTxtConfig(opts)); -} diff --git a/packages/astro-ghostcms/src/integrations/rssfeed/astro-ghostcms-rss.ts b/packages/astro-ghostcms/src/integrations/rssfeed/astro-ghostcms-rss.ts new file mode 100644 index 00000000..3828f8c6 --- /dev/null +++ b/packages/astro-ghostcms/src/integrations/rssfeed/astro-ghostcms-rss.ts @@ -0,0 +1,52 @@ +import { createResolver, defineIntegration } from "astro-integration-kit"; +import { corePlugins } from "astro-integration-kit/plugins"; +import { z } from "astro/zod"; +import c from "picocolors"; + +export default defineIntegration({ + name: "@matthiesenxyz/astro-ghostcms-rss", + optionsSchema: z.object({ + verbose: z.coerce.boolean().optional(), + }), + plugins: [...corePlugins], + setup({ options }) { + 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", + )}`, + ); + + RSSLogger.info(c.bold(c.magenta("RSS Feed Enabled. Setting up..."))); + + injectRoute({ + pattern: "/rss-style.xsl", + entrypoint: resolve("./routes/rss-style.xsl.ts"), + }); + + injectRoute({ + pattern: "/rss.xml", + entrypoint: resolve("./routes/rss.xml.ts"), + }); + }, + "astro:config:done": ({ logger }) => { + const RSSLogger = logger.fork( + `${c.bold(c.blue("👻 Astro-GhostCMS"))}${c.gray("/")}${c.blue( + "RSSGenerator", + )}`, + ); + const verboseLogsInfo = (message:string) => { + if (options.verbose) { + RSSLogger.info(message); + } + }; + + verboseLogsInfo(c.bold(c.green("RSS Feed Setup Complete"))); + }, + }; + }, +}); diff --git a/packages/astro-ghostcms/src/integrations/rssfeed/index.ts b/packages/astro-ghostcms/src/integrations/rssfeed/index.ts new file mode 100644 index 00000000..34ef34e7 --- /dev/null +++ b/packages/astro-ghostcms/src/integrations/rssfeed/index.ts @@ -0,0 +1,3 @@ +import astroGhostcmsRSS from "./astro-ghostcms-rss"; + +export default astroGhostcmsRSS; diff --git a/packages/astro-ghostcms/src/default-routes/rss-style.xsl.ts b/packages/astro-ghostcms/src/integrations/rssfeed/routes/rss-style.xsl.ts similarity index 100% rename from packages/astro-ghostcms/src/default-routes/rss-style.xsl.ts rename to packages/astro-ghostcms/src/integrations/rssfeed/routes/rss-style.xsl.ts diff --git a/packages/astro-ghostcms/src/default-routes/rss.xml.ts b/packages/astro-ghostcms/src/integrations/rssfeed/routes/rss.xml.ts similarity index 90% rename from packages/astro-ghostcms/src/default-routes/rss.xml.ts rename to packages/astro-ghostcms/src/integrations/rssfeed/routes/rss.xml.ts index 3ca72a34..860f5e2c 100644 --- a/packages/astro-ghostcms/src/default-routes/rss.xml.ts +++ b/packages/astro-ghostcms/src/integrations/rssfeed/routes/rss.xml.ts @@ -1,6 +1,6 @@ import rss from "@astrojs/rss"; import type { APIContext } from "astro"; -import { getAllPosts, getSettings, invariant } from "../api"; +import { getAllPosts, getSettings, invariant } from "../../../api"; const posts = await getAllPosts(); const settings = await getSettings(); diff --git a/packages/astro-ghostcms/src/integrations/satoriog/astro-ghostcms-satoriog.ts b/packages/astro-ghostcms/src/integrations/satoriog/astro-ghostcms-satoriog.ts new file mode 100644 index 00000000..eba963b3 --- /dev/null +++ b/packages/astro-ghostcms/src/integrations/satoriog/astro-ghostcms-satoriog.ts @@ -0,0 +1,84 @@ +import { createResolver, defineIntegration } from "astro-integration-kit"; +import { corePlugins } from "astro-integration-kit/plugins"; +import { z } from "astro/zod"; +import c from "picocolors"; + +export default defineIntegration({ + name: "@matthiesenxyz/astro-ghostcms-satoriog", + optionsSchema: z.object({ + verbose: z.coerce.boolean().optional(), + }), + plugins: [...corePlugins], + setup({ options }) { + const { resolve } = createResolver(import.meta.url); + + return { + "astro:config:setup": ({ + watchIntegration, + updateConfig, + injectRoute, + logger, + }) => { + watchIntegration(resolve()); + + 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...")), + ); + + injectRoute({ + pattern: "/open-graph/[slug].png", + entrypoint: resolve("./routes/[slug].png.ts"), + }); + + injectRoute({ + pattern: "/open-graph/index.png", + entrypoint: resolve("./routes/index.png.ts"), + }); + + injectRoute({ + pattern: "/open-graph/authors.png", + entrypoint: resolve("./routes/authors.png.ts"), + }); + + injectRoute({ + pattern: "/open-graph/author/[slug].png", + entrypoint: resolve("./routes/author/[slug].png.ts"), + }); + + injectRoute({ + pattern: "/open-graph/tags.png", + entrypoint: resolve("./routes/tags.png.ts"), + }); + + injectRoute({ + pattern: "/open-graph/tag/[slug].png", + entrypoint: resolve("./routes/tag/[slug].png.ts"), + }); + + updateConfig({ + 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 verboseLogsInfo = (message:string) => { + if (options.verbose) { + SatoriLogger.info(message); + } + }; + + verboseLogsInfo(c.bold(c.green("OG Image Integration Setup Complete"))); + }, + }; + }, +}); diff --git a/packages/astro-ghostcms/src/integrations/satoriog/index.ts b/packages/astro-ghostcms/src/integrations/satoriog/index.ts new file mode 100644 index 00000000..20a40a55 --- /dev/null +++ b/packages/astro-ghostcms/src/integrations/satoriog/index.ts @@ -0,0 +1,3 @@ +import astroGhostcmsSatoriog from "./astro-ghostcms-satoriog"; + +export default astroGhostcmsSatoriog; diff --git a/packages/astro-ghostcms/src/default-routes/open-graph/[slug].png.ts b/packages/astro-ghostcms/src/integrations/satoriog/routes/[slug].png.ts similarity index 93% rename from packages/astro-ghostcms/src/default-routes/open-graph/[slug].png.ts rename to packages/astro-ghostcms/src/integrations/satoriog/routes/[slug].png.ts index 3be6ca55..fdc1c53c 100644 --- a/packages/astro-ghostcms/src/default-routes/open-graph/[slug].png.ts +++ b/packages/astro-ghostcms/src/integrations/satoriog/routes/[slug].png.ts @@ -5,13 +5,8 @@ import type { InferGetStaticPropsType, } from "astro"; import { html } from "satori-html"; -import { - getAllPages, - getAllPosts, - getSettings, - invariant, -} from "../../api/index.js"; -import satoriOG from "../../integrations/satori.js"; +import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api"; +import satoriOG from "../satori"; export const getStaticPaths: GetStaticPaths = async () => { const result: GetStaticPathsItem[] = []; diff --git a/packages/astro-ghostcms/src/default-routes/open-graph/author/[slug].png.ts b/packages/astro-ghostcms/src/integrations/satoriog/routes/author/[slug].png.ts similarity index 96% rename from packages/astro-ghostcms/src/default-routes/open-graph/author/[slug].png.ts rename to packages/astro-ghostcms/src/integrations/satoriog/routes/author/[slug].png.ts index 95046eba..2853b8ea 100644 --- a/packages/astro-ghostcms/src/default-routes/open-graph/author/[slug].png.ts +++ b/packages/astro-ghostcms/src/integrations/satoriog/routes/author/[slug].png.ts @@ -10,8 +10,8 @@ import { getAllPosts, getSettings, invariant, -} from "../../../api/index.js"; -import satoriOG from "../../../integrations/satori.js"; +} from "../../../../api"; +import satoriOG from "../../satori"; export const getStaticPaths: GetStaticPaths = async () => { const result: GetStaticPathsItem[] = []; diff --git a/packages/astro-ghostcms/src/default-routes/open-graph/authors.png.ts b/packages/astro-ghostcms/src/integrations/satoriog/routes/authors.png.ts similarity index 93% rename from packages/astro-ghostcms/src/default-routes/open-graph/authors.png.ts rename to packages/astro-ghostcms/src/integrations/satoriog/routes/authors.png.ts index 9003a7b7..36166085 100644 --- a/packages/astro-ghostcms/src/default-routes/open-graph/authors.png.ts +++ b/packages/astro-ghostcms/src/integrations/satoriog/routes/authors.png.ts @@ -5,13 +5,8 @@ import type { InferGetStaticPropsType, } from "astro"; import { html } from "satori-html"; -import { - getAllPages, - getAllPosts, - getSettings, - invariant, -} from "../../api/index.js"; -import satoriOG from "../../integrations/satori.js"; +import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api"; +import satoriOG from "../satori"; export const getStaticPaths: GetStaticPaths = async () => { const result: GetStaticPathsItem[] = []; diff --git a/packages/astro-ghostcms/src/default-routes/open-graph/index.png.ts b/packages/astro-ghostcms/src/integrations/satoriog/routes/index.png.ts similarity index 93% rename from packages/astro-ghostcms/src/default-routes/open-graph/index.png.ts rename to packages/astro-ghostcms/src/integrations/satoriog/routes/index.png.ts index 4059cb6c..8574fd49 100644 --- a/packages/astro-ghostcms/src/default-routes/open-graph/index.png.ts +++ b/packages/astro-ghostcms/src/integrations/satoriog/routes/index.png.ts @@ -5,13 +5,8 @@ import type { InferGetStaticPropsType, } from "astro"; import { html } from "satori-html"; -import { - getAllPages, - getAllPosts, - getSettings, - invariant, -} from "../../api/index.js"; -import satoriOG from "../../integrations/satori.js"; +import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api"; +import satoriOG from "../satori"; export const getStaticPaths: GetStaticPaths = async () => { const result: GetStaticPathsItem[] = []; diff --git a/packages/astro-ghostcms/src/default-routes/open-graph/tag/[slug].png.ts b/packages/astro-ghostcms/src/integrations/satoriog/routes/tag/[slug].png.ts similarity index 96% rename from packages/astro-ghostcms/src/default-routes/open-graph/tag/[slug].png.ts rename to packages/astro-ghostcms/src/integrations/satoriog/routes/tag/[slug].png.ts index f535f543..9076f235 100644 --- a/packages/astro-ghostcms/src/default-routes/open-graph/tag/[slug].png.ts +++ b/packages/astro-ghostcms/src/integrations/satoriog/routes/tag/[slug].png.ts @@ -10,8 +10,8 @@ import { getAllTags, getSettings, invariant, -} from "../../../api/index.js"; -import satoriOG from "../../../integrations/satori.js"; +} from "../../../../api"; +import satoriOG from "../../satori"; export const getStaticPaths: GetStaticPaths = async () => { const result: GetStaticPathsItem[] = []; diff --git a/packages/astro-ghostcms/src/default-routes/open-graph/tags.png.ts b/packages/astro-ghostcms/src/integrations/satoriog/routes/tags.png.ts similarity index 93% rename from packages/astro-ghostcms/src/default-routes/open-graph/tags.png.ts rename to packages/astro-ghostcms/src/integrations/satoriog/routes/tags.png.ts index d81e0d2e..5123a0c9 100644 --- a/packages/astro-ghostcms/src/default-routes/open-graph/tags.png.ts +++ b/packages/astro-ghostcms/src/integrations/satoriog/routes/tags.png.ts @@ -5,13 +5,8 @@ import type { InferGetStaticPropsType, } from "astro"; import { html } from "satori-html"; -import { - getAllPages, - getAllPosts, - getSettings, - invariant, -} from "../../api/index.js"; -import satoriOG from "../../integrations/satori.js"; +import { getAllPages, getAllPosts, getSettings, invariant } from "../../../api"; +import satoriOG from "../satori"; export const getStaticPaths: GetStaticPaths = async () => { const result: GetStaticPathsItem[] = []; diff --git a/packages/astro-ghostcms/src/integrations/satori.ts b/packages/astro-ghostcms/src/integrations/satoriog/satori.ts similarity index 98% rename from packages/astro-ghostcms/src/integrations/satori.ts rename to packages/astro-ghostcms/src/integrations/satoriog/satori.ts index 402eab9c..80ced69d 100644 --- a/packages/astro-ghostcms/src/integrations/satori.ts +++ b/packages/astro-ghostcms/src/integrations/satoriog/satori.ts @@ -5,7 +5,7 @@ import type { ToImageOptions, ToResponseOptions, ToSvgOptions, -} from "../../types.js"; +} from "./types"; const satoriOG = ({ width, height, template }: SatoriAstroOGOptions) => { return { diff --git a/packages/astro-ghostcms/types.d.ts b/packages/astro-ghostcms/src/integrations/satoriog/types.d.ts similarity index 65% rename from packages/astro-ghostcms/types.d.ts rename to packages/astro-ghostcms/src/integrations/satoriog/types.d.ts index b991c890..9e613b6f 100644 --- a/packages/astro-ghostcms/types.d.ts +++ b/packages/astro-ghostcms/src/integrations/satoriog/types.d.ts @@ -1,24 +1,5 @@ import type { Resvg } from "@resvg/resvg-js"; import type satori from "satori"; - -export type { UserConfig, GhostUserConfig } from "./src/schemas"; - -export type { - Author, - AuthorsIncludeSchema, - Page, - PagesIncludeSchema, - Post, - PostsIncludeSchema, - Settings, - Tag, - TagsIncludeSchema, - Tier, - TiersIncludeSchema, -} from "./src/api/index.ts"; - -export type { ContentAPICredentials, APIVersions } from "@ts-ghost/core-api"; - type SatoriParameters = Parameters; type SatoriOptions = SatoriParameters[1]; type ResvgOptions = NonNullable[1]>; diff --git a/packages/astro-ghostcms/src/integrations/sitemap.ts b/packages/astro-ghostcms/src/integrations/sitemap.ts deleted file mode 100644 index 0c174e54..00000000 --- a/packages/astro-ghostcms/src/integrations/sitemap.ts +++ /dev/null @@ -1,38 +0,0 @@ -import sitemap, { type SitemapOptions } from "@astrojs/sitemap"; -import type { UserConfig } from "../schemas"; - -export function getSitemapConfig(opts: UserConfig["sitemap"]): SitemapOptions { - const sitemapConfig: SitemapOptions = {}; - if (opts?.filter) { - sitemapConfig.filter = opts.filter; - } - if (opts?.changefreq) { - sitemapConfig.changefreq = opts.changefreq; - } - if (opts?.entryLimit) { - sitemapConfig.entryLimit = opts.entryLimit; - } - if (opts?.customPages) { - sitemapConfig.customPages = opts.customPages; - } - if (opts?.i18n) { - sitemapConfig.i18n = opts.i18n; - } - if (opts?.lastmod) { - sitemapConfig.lastmod = opts.lastmod; - } - if (opts?.priority) { - sitemapConfig.priority = opts.priority; - } - if (opts?.serialize) { - sitemapConfig.serialize = opts.serialize; - } - return sitemapConfig; -} - -/** - * A wrapped version of the `@astrojs/sitemap` integration for GhostCMS. - */ -export default function ghostSitemap(opts: UserConfig["sitemap"]) { - return sitemap(getSitemapConfig(opts)); -} diff --git a/packages/astro-ghostcms/src/integrations/themeprovider/astro-ghostcms-themeprovider.ts b/packages/astro-ghostcms/src/integrations/themeprovider/astro-ghostcms-themeprovider.ts new file mode 100644 index 00000000..4a463756 --- /dev/null +++ b/packages/astro-ghostcms/src/integrations/themeprovider/astro-ghostcms-themeprovider.ts @@ -0,0 +1,99 @@ +import { createResolver, defineIntegration } from "astro-integration-kit"; +import { corePlugins } from "astro-integration-kit/plugins"; +import { z } from "astro/zod"; +import c from "picocolors"; + +export default defineIntegration({ + name: "@matthiesenxyz/astro-ghostcms-themeprovider", + optionsSchema: z.object({ + theme: z.string(), + verbose: z.coerce.boolean().optional(), + }), + plugins: [...corePlugins], + setup({ options }) { + const { resolve } = createResolver(import.meta.url); + + const DEFAULT_THEME = "@matthiesenxyz/astro-ghostcms-theme-default"; + + return { + "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 verboseLogsInfo = (message:string) => { + if (options.verbose) { + themeLogger.info(message); + } + }; + + + themeLogger.info( + c.bold(c.magenta("Theme Provider enabled. Setting up...")), + ); + + + if (options.theme === DEFAULT_THEME) { + verboseLogsInfo( + c.blue("No theme is set, injecting default theme"), + ); + } else { + verboseLogsInfo(`${c.bold(c.cyan("Injecting Theme:"))} ${c.bold(c.underline(c.magenta(options.theme)))}`); + } + + + injectRoute({ + pattern: "/", + entrypoint: `${options.theme}/index.astro`, + }); + + injectRoute({ + pattern: "/[slug]", + entrypoint: `${options.theme}/[slug].astro`, + }); + + injectRoute({ + pattern: "/tags", + entrypoint: `${options.theme}/tags.astro`, + }); + + injectRoute({ + pattern: "/authors", + entrypoint: `${options.theme}/authors.astro`, + }); + + injectRoute({ + pattern: "/tag/[slug]", + entrypoint: `${options.theme}/tag/[slug].astro`, + }); + + injectRoute({ + pattern: "/author/[slug]", + entrypoint: `${options.theme}/author/[slug].astro`, + }); + + injectRoute({ + 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 verboseLogsInfo = (message:string) => { + if (options.verbose) { + themeLogger.info(message); + } + }; + + verboseLogsInfo(c.bold(c.green("Provider Setup Complete"))); + }, + }; + }, +}); diff --git a/packages/astro-ghostcms/src/integrations/themeprovider/index.ts b/packages/astro-ghostcms/src/integrations/themeprovider/index.ts new file mode 100644 index 00000000..88e66a21 --- /dev/null +++ b/packages/astro-ghostcms/src/integrations/themeprovider/index.ts @@ -0,0 +1,3 @@ +import astroGhostcmsThemeProvider from "./astro-ghostcms-themeprovider"; + +export default astroGhostcmsThemeProvider; diff --git a/packages/astro-ghostcms/src/integrations/virtual-config.ts b/packages/astro-ghostcms/src/integrations/virtual-config.ts deleted file mode 100644 index e49f5f6e..00000000 --- a/packages/astro-ghostcms/src/integrations/virtual-config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { GhostUserConfig } from "../schemas"; -import config from "virtual:@matthiesenxyz/astro-ghostcms/config"; - -const UserConfig = config as GhostUserConfig; - -export default UserConfig; diff --git a/packages/astro-ghostcms/src/default-routes/404/404.astro b/packages/astro-ghostcms/src/routes/404.astro similarity index 88% rename from packages/astro-ghostcms/src/default-routes/404/404.astro rename to packages/astro-ghostcms/src/routes/404.astro index 8489240a..175a5093 100644 --- a/packages/astro-ghostcms/src/default-routes/404/404.astro +++ b/packages/astro-ghostcms/src/routes/404.astro @@ -1,6 +1,6 @@ --- import './404.css'; -import { getSettings, invariant } from '../../api'; +import { getSettings, invariant } from '../api'; const settings = await getSettings(); invariant(settings, "Settings not found"); diff --git a/packages/astro-ghostcms/src/default-routes/404/404.css b/packages/astro-ghostcms/src/routes/404.css similarity index 100% rename from packages/astro-ghostcms/src/default-routes/404/404.css rename to packages/astro-ghostcms/src/routes/404.css diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/authors/authors.test.ts b/packages/astro-ghostcms/src/schemas/api/authors.test.ts similarity index 95% rename from packages/astro-ghostcms/src/api/content-api/schemas/authors/authors.test.ts rename to packages/astro-ghostcms/src/schemas/api/authors.test.ts index 1d24851f..7cd4b54e 100644 --- a/packages/astro-ghostcms/src/api/content-api/schemas/authors/authors.test.ts +++ b/packages/astro-ghostcms/src/schemas/api/authors.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import createFetchMock from "vitest-fetch-mock"; -import TS_API from "../../content-api"; +import { TSGhostContentAPI } from "@ts-ghost/content-api"; const fetchMocker = createFetchMock(vi); @@ -9,7 +9,7 @@ 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 api = new TS_API(url, key, "v5.0"); + const api = new TSGhostContentAPI(url, key, "v5.0"); test(".browse() params shouldnt accept invalid params", () => { // @ts-expect-error - shouldnt accept invalid params const browse = api.authors.browse({ pp: 2 }); @@ -95,10 +95,10 @@ describe("authors api .browse() Args Type-safety", () => { }); describe("authors resource mocked", () => { - let api: TS_API; + let api: TSGhostContentAPI; beforeEach(() => { - api = new TS_API( + api = new TSGhostContentAPI( "https://my-ghost-blog.com", "59d4bf56c73c04a18c867dc3ba", "v5.0", diff --git a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/authors/authors.ts b/packages/astro-ghostcms/src/schemas/api/authors.ts similarity index 100% rename from packages/starlight-ghostcms/src/utils/api/content-api/schemas/authors/authors.ts rename to packages/astro-ghostcms/src/schemas/api/authors.ts diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/index.ts b/packages/astro-ghostcms/src/schemas/api/index.ts similarity index 85% rename from packages/astro-ghostcms/src/api/content-api/schemas/index.ts rename to packages/astro-ghostcms/src/schemas/api/index.ts index 88bfc1b9..43c252a2 100644 --- a/packages/astro-ghostcms/src/api/content-api/schemas/index.ts +++ b/packages/astro-ghostcms/src/schemas/api/index.ts @@ -1,7 +1,7 @@ export * from "./authors"; -export * from "./helpers"; export * from "./pages"; export * from "./posts"; export * from "./settings"; +export * from "./socials"; export * from "./tags"; export * from "./tiers"; diff --git a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/pages/pages.ts b/packages/astro-ghostcms/src/schemas/api/pages.ts similarity index 94% rename from packages/starlight-ghostcms/src/utils/api/content-api/schemas/pages/pages.ts rename to packages/astro-ghostcms/src/schemas/api/pages.ts index 2fcd9655..c688d91f 100644 --- a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/pages/pages.ts +++ b/packages/astro-ghostcms/src/schemas/api/pages.ts @@ -7,8 +7,8 @@ import { } from "@ts-ghost/core-api"; import { z } from "astro/zod"; -import { authorsSchema } from "../authors"; -import { tagsSchema } from "../tags"; +import { authorsSchema } from "./authors"; +import { tagsSchema } from "./tags"; const postsAuthorSchema = authorsSchema.extend({ url: z.string().nullish(), diff --git a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/posts/posts.test.ts b/packages/astro-ghostcms/src/schemas/api/posts.test.ts similarity index 87% rename from packages/starlight-ghostcms/src/utils/api/content-api/schemas/posts/posts.test.ts rename to packages/astro-ghostcms/src/schemas/api/posts.test.ts index b2dfbbe3..35427607 100644 --- a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/posts/posts.test.ts +++ b/packages/astro-ghostcms/src/schemas/api/posts.test.ts @@ -1,14 +1,14 @@ import { describe, expect, test } from "vitest"; -import TS_API from "../../content-api"; -import type { Post } from "./posts"; +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"; describe("posts api .browse() Args Type-safety", () => { - const api = new TS_API(url, key, "v5.0"); + const api = new TSGhostContentAPI(url, key, "v5.0"); test(".browse() params shouldnt accept invalid params", () => { // @ts-expect-error - shouldnt accept invalid params const browse = api.posts.browse({ pp: 2 }); @@ -21,8 +21,7 @@ describe("posts api .browse() Args Type-safety", () => { foo: true, } satisfies { [k in keyof Post]?: true | undefined }; - // biome-ignore lint/style/useConst: - let test = api.posts + const test = api.posts .browse() // @ts-expect-error - shouldnt accept invalid params .fields(outputFields); @@ -45,8 +44,7 @@ describe("posts api .browse() Args Type-safety", () => { title: true, } satisfies { [k in keyof Post]?: true | undefined }; - // biome-ignore lint/style/useConst: - let test = api.posts.browse().fields(outputFields); + const test = api.posts.browse().fields(outputFields); expect(test.getOutputFields()).toEqual(["slug", "title"]); // @ts-expect-error - shouldnt accept invalid params diff --git a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/posts/posts.ts b/packages/astro-ghostcms/src/schemas/api/posts.ts similarity index 95% rename from packages/starlight-ghostcms/src/utils/api/content-api/schemas/posts/posts.ts rename to packages/astro-ghostcms/src/schemas/api/posts.ts index b479ee17..cb514ed8 100644 --- a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/posts/posts.ts +++ b/packages/astro-ghostcms/src/schemas/api/posts.ts @@ -7,8 +7,8 @@ import { } from "@ts-ghost/core-api"; import { z } from "astro/zod"; -import { authorsSchema } from "../authors"; -import { tagsSchema } from "../tags"; +import { authorsSchema } from "./authors"; +import { tagsSchema } from "./tags"; const postsAuthorSchema = authorsSchema.extend({ url: z.string().nullish(), diff --git a/packages/astro-ghostcms/src/schemas/api/settings.test.ts b/packages/astro-ghostcms/src/schemas/api/settings.test.ts new file mode 100644 index 00000000..02d9f403 --- /dev/null +++ b/packages/astro-ghostcms/src/schemas/api/settings.test.ts @@ -0,0 +1,39 @@ +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"; + +describe("settings integration tests browse", () => { + let api: TSGhostContentAPI; + beforeEach(() => { + api = new TSGhostContentAPI(url, key, "v5.0"); + }); + test("settings.fetch()", async () => { + const result = await api.settings.fetch(); + expect(result).not.toBeUndefined(); + expect(result).not.toBeNull(); + if (!result.success) { + expect(result.errors).toBeDefined(); + expect(result.errors).toHaveLength(1); + } else { + expect(result.data).toBeDefined(); + const settings = result.data; + expect(settings).toBeDefined(); + 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.icon).toBeNull(); + expect(settings.lang).toBe("en"); + expect(settings.timezone).toBe("Etc/UTC"); + expect(settings.codeinjection_head).toBeNull(); + expect(settings.codeinjection_foot).toBeNull(); + expect(settings.members_support_address).toBe("noreply"); + } + }); +}); diff --git a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/settings/settings.ts b/packages/astro-ghostcms/src/schemas/api/settings.ts similarity index 100% rename from packages/starlight-ghostcms/src/utils/api/content-api/schemas/settings/settings.ts rename to packages/astro-ghostcms/src/schemas/api/settings.ts diff --git a/packages/astro-ghostcms/src/api/content-api/schemas/helpers/socials.ts b/packages/astro-ghostcms/src/schemas/api/socials.ts similarity index 100% rename from packages/astro-ghostcms/src/api/content-api/schemas/helpers/socials.ts rename to packages/astro-ghostcms/src/schemas/api/socials.ts diff --git a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/tags/tags.ts b/packages/astro-ghostcms/src/schemas/api/tags.ts similarity index 100% rename from packages/starlight-ghostcms/src/utils/api/content-api/schemas/tags/tags.ts rename to packages/astro-ghostcms/src/schemas/api/tags.ts diff --git a/packages/starlight-ghostcms/src/utils/api/content-api/schemas/tiers/tiers.ts b/packages/astro-ghostcms/src/schemas/api/tiers.ts similarity index 100% rename from packages/starlight-ghostcms/src/utils/api/content-api/schemas/tiers/tiers.ts rename to packages/astro-ghostcms/src/schemas/api/tiers.ts diff --git a/packages/astro-ghostcms/src/schemas/index.ts b/packages/astro-ghostcms/src/schemas/index.ts deleted file mode 100644 index eb3d58e3..00000000 --- a/packages/astro-ghostcms/src/schemas/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./robots"; -export * from "./sitemap"; -export * from "./userconfig"; diff --git a/packages/astro-ghostcms/src/schemas/robots.ts b/packages/astro-ghostcms/src/schemas/robots.ts deleted file mode 100644 index dd3c2da1..00000000 --- a/packages/astro-ghostcms/src/schemas/robots.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { z } from "astro/zod"; - -const RobotsPolicySchema = z.object({ - /** You must provide a name of the automatic client (search engine crawler). - * Wildcards are allowed. - */ - userAgent: z.string(), - /** Allowed paths for crawling */ - allow: z.string().optional(), - /** Disallowed paths for crawling */ - disallow: z.string().optional(), - /** Indicates that the page's URL contains parameters that should be ignored during crawling. - * Maximum string length is limited to 500. - */ - cleanParam: z.string().optional(), - /** Minimum interval (in secs) for the crawler to wait after loading one page, before starting other */ - crawlDelay: z.number().optional(), -}); - -export const RobotsTxtSchema = z.object({ - /** @example host: true - * // Automatically resolve using the site option from Astro config - * @example host: 'example.com' - */ - host: z.string().optional(), - /** @example sitemap: "https://example.com/sitemap-0.xml" - * @example sitemap: ['https://example.com/sitemap-0.xml','https://example.com/sitemap-1.xml'] - * @example sitemap: false - If you want to get the robots.txt file without the Sitemap: ... entry, set the sitemap parameter to false. - */ - sitemap: z.union([z.string(), z.string().array(), z.boolean()]).optional(), - /** astrojs/sitemap and astro-robots-txt integrations have the sitemap-index.xml as their primary output. That is why the default value of sitemapBaseFileName is set to sitemap-index. - * @example sitemapBaseFileName: 'custom-sitemap' - */ - sitemapBaseFileName: z.string().optional(), - /** SET POLICY RULES */ - policy: RobotsPolicySchema.array().optional(), -}); diff --git a/packages/astro-ghostcms/src/schemas/sitemap.ts b/packages/astro-ghostcms/src/schemas/sitemap.ts deleted file mode 100644 index f94789e9..00000000 --- a/packages/astro-ghostcms/src/schemas/sitemap.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { z } from "astro/zod"; - -const localeKeySchema = z.string().min(1); -enum EnumChangefreq { - DAILY = "daily", - MONTHLY = "monthly", - ALWAYS = "always", - HOURLY = "hourly", - WEEKLY = "weekly", - YEARLY = "yearly", - NEVER = "never", -} -export const SitemapSchema = z.object({ - filter: z.function().args(z.string()).returns(z.boolean()).optional(), - customPages: z.string().url().array().optional(), - i18n: z - .object({ - defaultLocale: localeKeySchema, - locales: z.record( - localeKeySchema, - z - .string() - .min(2) - .regex(/^[a-zA-Z\-]+$/gm, { - message: "Only English alphabet symbols and hyphen allowed", - }), - ), - }) - .refine((val) => !val || val.locales[val.defaultLocale], { - message: "`defaultLocale` must exist in `locales` keys", - }) - .optional(), - entryLimit: z.number().nonnegative().optional(), - serialize: z.function().args(z.any()).returns(z.any()).optional(), - changefreq: z.nativeEnum(EnumChangefreq).optional(), - lastmod: z.date().optional(), - priority: z.number().min(0).max(1).optional(), -}); diff --git a/packages/astro-ghostcms/src/schemas/userconfig.test.ts b/packages/astro-ghostcms/src/schemas/userconfig.test.ts new file mode 100644 index 00000000..1c394da8 --- /dev/null +++ b/packages/astro-ghostcms/src/schemas/userconfig.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from "vitest"; +import { GhostUserConfigSchema } from "./userconfig"; + +describe("GhostUserConfigSchema", () => { + it("should validate a valid user config", () => { + const validConfig = { + ghostURL: "https://ghostdemo.matthiesen.xyz", + ThemeProvider: { + disableThemeProvider: true, + theme: "@matthiesenxyz/astro-ghostcms-theme-default", + }, + disableDefault404: false, + enableRSSFeed: true, + enableOGImages: true, + verbose: false, + Integrations: {}, + }; + + const result = GhostUserConfigSchema.safeParse(validConfig); + + expect(result.success).to.be.true; + // @ts-expect-error + expect(result.data).to.deep.equal(validConfig); + }); + + it("should invalidate an invalid user config", () => { + const invalidConfig = { + ghostURL: "invalid-url", + ThemeProvider: { + disableThemeProvider: "true", + theme: 123, + }, + disableDefault404: "false", + enableRSSFeed: "true", + enableOGImages: "true", + Integrations: { + sitemap: { + // invalid sitemap configuration + }, + robotstxt: { + // invalid robotstxt configuration + }, + }, + verbose: "false", + }; + + const result = GhostUserConfigSchema.safeParse(invalidConfig); + + expect(result.success).to.be.false; + expect(!result.success).to.exist; + }); +}); diff --git a/packages/astro-ghostcms/src/schemas/userconfig.ts b/packages/astro-ghostcms/src/schemas/userconfig.ts index b949e271..10ad2a75 100644 --- a/packages/astro-ghostcms/src/schemas/userconfig.ts +++ b/packages/astro-ghostcms/src/schemas/userconfig.ts @@ -1,7 +1,8 @@ +import type { SitemapOptions } from "@astrojs/sitemap"; +import type { RobotsTxtOptions } from "astro-robots-txt"; import { z } from "astro/zod"; -import { RobotsTxtSchema, SitemapSchema } from "./index"; -export const UserConfigSchema = z.object({ +export const GhostUserConfigSchema = z.object({ /** OPTIONAL - Either set the URL in your .env or put it here * @example * // https://astro.build/config @@ -12,46 +13,59 @@ export const UserConfigSchema = z.object({ * }) * ], * }); */ - ghostURL: z.string().url().optional(), - /** OPTIONAL - Allows the user to disable the `/rss.xml` injection */ - disableRSS: z.boolean().default(false), - /** OPTIONAL - Allows the user to disable the `/open-graph/*` route injection - * @ This feature uses `satori` to generate OpenGraph Images + ghostURL: z.coerce.string().url("Must be a URL").optional(), + /** OPTIONAL - Configure the Theme Provider + * @ This option allows the user to configure the Theme Provider */ - disableSatoriOG: z.boolean().default(false), - /** OPTIONAL - Allows the user to disable the `/404` injection */ - disable404: z.boolean().default(false), - /** OPTIONAL - Disable Route Injector - * This option allows the user to disable the route injection system and utilize just the integraions other functions. Such as API, sitemap and robotstxt integrations. */ - disableRouteInjection: z.boolean().default(false), - /** OPTIONAL - (Default: true) Allows the user to disable "info" console output */ - disableConsoleOutput: z.boolean().default(true), - /** OPTIONAL - Theme Selector - * This option allows the user to replace the included theme with an external npm module - * @example - * // https://astro.build/config - * export default defineConfig({ - * integrations: [ - * ghostcms({ - * theme: "@matthiesenxyz/astro-ghostcms-theme-default" - * }) - * ], - * }); */ - theme: z.string().default("@matthiesenxyz/astro-ghostcms-theme-default"), - /** OPTIONAL - astrojs/sitemap - * This option allows the user to configure the included integration + ThemeProvider: z + .object({ + /** OPTIONAL - Disable the theme provider + * @default false + */ + disableThemeProvider: z.coerce.boolean(), + /** OPTIONAL - Set the theme you want to use + * @default "@matthiesenxyz/astro-ghostcms-theme-default" + */ + theme: z.string(), + }) + .optional() + .default({ + disableThemeProvider: false, + theme: "@matthiesenxyz/astro-ghostcms-theme-default" + }), + /** Allows the user to disable the provided 404 page */ + disableDefault404: z.coerce.boolean().optional(), + /** Allows the user to disable the provided RSS Feed */ + enableRSSFeed: z.coerce.boolean().optional().default(true), + /** Allows the user to Enable or Disable the default Satori OG Image Generation + * @default true + */ + enableOGImages: z.coerce.boolean().optional().default(true), + /** Allows the user to turn on/off Full Console Logs + * @default true + */ + verbose: z.coerce.boolean().optional(), + /** OPTIONAL - Integrations Configuration + * This option allows the user to configure the included integrations * Options shown are the availble options - * REFERENCE https://docs.astro.build/en/guides/integrations-guide/sitemap */ - sitemap: SitemapSchema.optional(), - /** OPTIONAL - astro-robots-txt - * This option allows the user to configure the included integration - * Options shown are the availble options - * REFERENCE https://www.npmjs.com/package/astro-robots-txt#configuration - */ - robotstxt: RobotsTxtSchema.optional(), + Integrations: z + .object({ + /** Optional - astro-robots-txt + * This option allows the user to configure the included integration + * Options shown are the availble options + * @see https://www.npmjs.com/package/astro-robots-txt#configuration + */ + robotsTxt: z.custom().optional(), + /** OPTIONAL - astrojs/sitemap + * This option allows the user to configure the included integration + * Options shown are the availble options + * @see https://docs.astro.build/en/guides/integrations-guide/sitemap + */ + sitemap: z.custom().optional(), + }) + .optional().default({}), }); /** USER CONFIGURATION SCHEMA */ -export type UserConfig = z.infer; -export type GhostUserConfig = z.infer; +export type GhostUserConfig = z.infer; diff --git a/packages/astro-ghostcms/src/utils/add-virtual-import.test.ts b/packages/astro-ghostcms/src/utils/add-virtual-import.test.ts deleted file mode 100644 index 8e2374dd..00000000 --- a/packages/astro-ghostcms/src/utils/add-virtual-import.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** MIT License - -Copyright (c) 2024 Florian Lefebvre - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. */ -import { type Mock, afterEach, describe, expect, test, vi } from "vitest"; -import { addVirtualImport } from "./add-virtual-import.js"; -import { addVitePlugin } from "./add-vite-plugin.js"; - -vi.mock("./add-vite-plugin"); - -const pluginNameStub = (name: T): `vite-plugin-${T}` => - `vite-plugin-${name}`; - -describe("add-virtual-import", () => { - const name = "test-module"; - const content = "export default {}"; - - afterEach(() => { - vi.clearAllMocks(); - }); - - test("It should call `addVitePlugin`", () => { - const updateConfig = vi.fn(); - - addVirtualImport({ - name, - content, - updateConfig, - }); - - expect(addVitePlugin).toHaveBeenCalled(); - }); - - test("`addVitePlugin` should get called with the correct plugin name", () => { - const updateConfig = vi.fn(); - - addVirtualImport({ - name, - content, - updateConfig, - }); - - const expectedName = pluginNameStub(name); - - const { plugin } = (addVitePlugin as Mock).mock.lastCall[0]; - - expect(plugin.name).toEqual(expectedName); - }); - - test("Virtual module should resolve correct name", () => { - const updateConfig = vi.fn(); - - addVirtualImport({ - name, - content, - updateConfig, - }); - - const { plugin } = (addVitePlugin as Mock).mock.lastCall[0]; - - const resolvedVirtualModuleId = plugin.resolveId(name); - - expect(resolvedVirtualModuleId).toEqual(`\0${name}`); - }); -}); diff --git a/packages/astro-ghostcms/src/utils/add-virtual-import.ts b/packages/astro-ghostcms/src/utils/add-virtual-import.ts deleted file mode 100644 index c655f947..00000000 --- a/packages/astro-ghostcms/src/utils/add-virtual-import.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** MIT License - -Copyright (c) 2024 Florian Lefebvre - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. */ -import type { HookParameters } from "astro"; -import type { Plugin } from "vite"; -import { addVitePlugin } from "./add-vite-plugin.js"; - -const resolveVirtualModuleId = (id: T): `\0${T}` => { - return `\0${id}`; -}; - -const createVirtualModule = (name: string, content: string): Plugin => { - const pluginName = `vite-plugin-${name}`; - - return { - name: pluginName, - resolveId(id) { - if (id === name) { - return resolveVirtualModuleId(id); - } - }, - load(id) { - if (id === resolveVirtualModuleId(name)) { - return content; - } - }, - }; -}; - -/** - * Creates a Vite virtual module and updates the Astro config. - * Virtual imports are useful for passing things like config options, or data computed within the integration. - * - * @param {object} params - * @param {string} params.name - * @param {string} params.content - * @param {import("astro").HookParameters<"astro:config:setup">["updateConfig"]} params.updateConfig - * - * @see https://astro-integration-kit.netlify.app/utilities/add-virtual-import/ - * - * @example - * ```ts - * // my-integration/index.ts - * import { addVirtualImport } from "astro-integration-kit"; - * - * addVirtualImport( - * name: 'virtual:my-integration/config', - * content: `export default ${ JSON.stringify({foo: "bar"}) }`, - * updateConfig - * ); - * ``` - * - * This is then readable anywhere else in your integration: - * - * ```ts - * // myIntegration/src/component/layout.astro - * import config from "virtual:my-integration/config"; - * - * console.log(config.foo) // "bar" - * ``` - */ -export const addVirtualImport = ({ - name, - content, - updateConfig, -}: { - name: string; - content: string; - updateConfig: HookParameters<"astro:config:setup">["updateConfig"]; -}) => { - addVitePlugin({ - plugin: createVirtualModule(name, content), - updateConfig, - }); -}; diff --git a/packages/astro-ghostcms/src/utils/add-vite-plugin.test.ts b/packages/astro-ghostcms/src/utils/add-vite-plugin.test.ts deleted file mode 100644 index 203f888a..00000000 --- a/packages/astro-ghostcms/src/utils/add-vite-plugin.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { Plugin } from "vite"; -import { describe, expect, test, vi } from "vitest"; -import { addVitePlugin } from "./add-vite-plugin.js"; - -describe("addVitePlugin", () => { - test("Should run", () => { - const updateConfig = vi.fn(); - - expect(() => - addVitePlugin({ - plugin: null, - updateConfig, - }), - ).not.toThrow(); - }); - - test("Should call updateConfig", () => { - const updateConfig = vi.fn(); - - addVitePlugin({ - plugin: null, - updateConfig, - }); - - expect(updateConfig).toHaveBeenCalled(); - }); - - test("Should add vite plugin", () => { - let plugin: Plugin; - const pluginName = "test-plugin"; - - const updateConfig = vi.fn((config) => { - plugin = config.vite.plugins[0]; - }); - - const expectedPlugin = { - name: pluginName, - }; - - addVitePlugin({ - plugin: expectedPlugin, - updateConfig, - }); - - // @ts-ignore - expect(plugin).toBeDefined(); - }); - - test("Plugin name should match", () => { - let plugin: Plugin; - const pluginName = "test-plugin"; - - const updateConfig = vi.fn((config) => { - plugin = config.vite.plugins[0]; - }); - - const expectedPlugin = { - name: pluginName, - }; - - addVitePlugin({ - plugin: expectedPlugin, - updateConfig, - }); - - // @ts-ignore - expect(plugin.name).toBe(pluginName); - }); -}); diff --git a/packages/astro-ghostcms/src/utils/add-vite-plugin.ts b/packages/astro-ghostcms/src/utils/add-vite-plugin.ts deleted file mode 100644 index 8d0279ba..00000000 --- a/packages/astro-ghostcms/src/utils/add-vite-plugin.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** MIT License - -Copyright (c) 2024 Florian Lefebvre - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. */ -import type { HookParameters } from "astro"; -import type { PluginOption } from "vite"; - -/** - * Adds a [vite plugin](https://vitejs.dev/guide/using-plugins) to the - * Astro config. - * - * @param {Params} params - * @param {import("vite").Plugin} params.plugin - * @param {import("astro").HookParameters<"astro:config:setup">["updateConfig"]} params.updateConfig - * - * @see https://astro-integration-kit.netlify.app/utilities/add-vite-plugin/ - * - * @example - * ```ts - * addVitePlugin({ - * plugin, - * updateConfig - * }) - * ``` - */ -export const addVitePlugin = ({ - plugin, - updateConfig, -}: { - plugin: PluginOption; - updateConfig: HookParameters<"astro:config:setup">["updateConfig"]; -}) => { - updateConfig({ - vite: { - plugins: [plugin], - }, - }); -}; diff --git a/packages/astro-ghostcms/src/utils/zod-validation/ValidationError.js b/packages/astro-ghostcms/src/utils/zod-validation/ValidationError.js deleted file mode 100644 index ff3e97ce..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/ValidationError.js +++ /dev/null @@ -1,22 +0,0 @@ -import * as zod from "zod"; -export class ValidationError extends Error { - name; - details; - constructor(message, options) { - super(message, options); - this.name = "ZodValidationError"; - this.details = getIssuesFromErrorOptions(options); - } - toString() { - return this.message; - } -} -function getIssuesFromErrorOptions(options) { - if (options) { - const cause = options.cause; - if (cause instanceof zod.ZodError) { - return cause.issues; - } - } - return []; -} diff --git a/packages/astro-ghostcms/src/utils/zod-validation/config.js b/packages/astro-ghostcms/src/utils/zod-validation/config.js deleted file mode 100644 index c3d9c30a..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/config.js +++ /dev/null @@ -1,5 +0,0 @@ -export const ISSUE_SEPARATOR = "; "; -export const MAX_ISSUES_IN_MESSAGE = 99; -export const PREFIX = "Validation error"; -export const PREFIX_SEPARATOR = ": "; -export const UNION_SEPARATOR = ", or "; diff --git a/packages/astro-ghostcms/src/utils/zod-validation/errorMap.js b/packages/astro-ghostcms/src/utils/zod-validation/errorMap.js deleted file mode 100644 index dae66709..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/errorMap.js +++ /dev/null @@ -1,10 +0,0 @@ -import { fromZodIssue } from "./fromZodIssue"; -export const errorMap = (issue, ctx) => { - const error = fromZodIssue({ - ...issue, - message: issue.message ?? ctx.defaultError, - }); - return { - message: error.message, - }; -}; diff --git a/packages/astro-ghostcms/src/utils/zod-validation/fromZodError.js b/packages/astro-ghostcms/src/utils/zod-validation/fromZodError.js deleted file mode 100644 index e5a0e383..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/fromZodError.js +++ /dev/null @@ -1,37 +0,0 @@ -import { ValidationError } from "./ValidationError"; -import { - ISSUE_SEPARATOR, - MAX_ISSUES_IN_MESSAGE, - PREFIX, - PREFIX_SEPARATOR, - UNION_SEPARATOR, -} from "./config"; -import { getMessageFromZodIssue } from "./fromZodIssue"; -import { prefixMessage } from "./prefixMessage"; -export function fromZodError(zodError, options = {}) { - const { - maxIssuesInMessage = MAX_ISSUES_IN_MESSAGE, - issueSeparator = ISSUE_SEPARATOR, - unionSeparator = UNION_SEPARATOR, - prefixSeparator = PREFIX_SEPARATOR, - prefix = PREFIX, - includePath = true, - } = options; - const zodIssues = zodError.errors; - const reason = - zodIssues.length === 0 - ? zodError.message - : zodIssues - .slice(0, maxIssuesInMessage) - .map((issue) => - getMessageFromZodIssue({ - issue, - issueSeparator, - unionSeparator, - includePath, - }), - ) - .join(issueSeparator); - const message = prefixMessage(reason, prefix, prefixSeparator); - return new ValidationError(message, { cause: zodError }); -} diff --git a/packages/astro-ghostcms/src/utils/zod-validation/fromZodIssue.js b/packages/astro-ghostcms/src/utils/zod-validation/fromZodIssue.js deleted file mode 100644 index fa5dddc5..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/fromZodIssue.js +++ /dev/null @@ -1,61 +0,0 @@ -import * as zod from "zod"; -import { ValidationError } from "./ValidationError"; -import { - ISSUE_SEPARATOR, - PREFIX, - PREFIX_SEPARATOR, - UNION_SEPARATOR, -} from "./config"; -import { prefixMessage } from "./prefixMessage"; -import { isNonEmptyArray } from "./utils/NonEmptyArray"; -import { joinPath } from "./utils/joinPath"; -export function getMessageFromZodIssue(props) { - const { issue, issueSeparator, unionSeparator, includePath } = props; - if (issue.code === "invalid_union") { - return issue.unionErrors - .reduce((acc, zodError) => { - const newIssues = zodError.issues - .map((issue) => - getMessageFromZodIssue({ - issue, - issueSeparator, - unionSeparator, - includePath, - }), - ) - .join(issueSeparator); - if (!acc.includes(newIssues)) { - acc.push(newIssues); - } - return acc; - }, []) - .join(unionSeparator); - } - if (includePath && isNonEmptyArray(issue.path)) { - if (issue.path.length === 1) { - const identifier = issue.path[0]; - if (typeof identifier === "number") { - return `${issue.message} at index ${identifier}`; - } - } - return `${issue.message} at "${joinPath(issue.path)}"`; - } - return issue.message; -} -export function fromZodIssue(issue, options = {}) { - const { - issueSeparator = ISSUE_SEPARATOR, - unionSeparator = UNION_SEPARATOR, - prefixSeparator = PREFIX_SEPARATOR, - prefix = PREFIX, - includePath = true, - } = options; - const reason = getMessageFromZodIssue({ - issue, - issueSeparator, - unionSeparator, - includePath, - }); - const message = prefixMessage(reason, prefix, prefixSeparator); - return new ValidationError(message, { cause: new zod.ZodError([issue]) }); -} diff --git a/packages/astro-ghostcms/src/utils/zod-validation/index.js b/packages/astro-ghostcms/src/utils/zod-validation/index.js deleted file mode 100644 index 3c273d4a..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export { ValidationError } from "./ValidationError"; -export { isValidationError } from "./isValidationError"; -export { isValidationErrorLike } from "./isValidationErrorLike"; -export { errorMap } from "./errorMap"; -export { fromZodIssue } from "./fromZodIssue"; -export { fromZodError } from "./fromZodError"; -export { toValidationError } from "./toValidationError"; diff --git a/packages/astro-ghostcms/src/utils/zod-validation/isValidationError.js b/packages/astro-ghostcms/src/utils/zod-validation/isValidationError.js deleted file mode 100644 index 4df3123c..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/isValidationError.js +++ /dev/null @@ -1,4 +0,0 @@ -import { ValidationError } from "./ValidationError"; -export function isValidationError(err) { - return err instanceof ValidationError; -} diff --git a/packages/astro-ghostcms/src/utils/zod-validation/isValidationErrorLike.js b/packages/astro-ghostcms/src/utils/zod-validation/isValidationErrorLike.js deleted file mode 100644 index 120a726b..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/isValidationErrorLike.js +++ /dev/null @@ -1,3 +0,0 @@ -export function isValidationErrorLike(err) { - return err instanceof Error && err.name === "ZodValidationError"; -} diff --git a/packages/astro-ghostcms/src/utils/zod-validation/prefixMessage.js b/packages/astro-ghostcms/src/utils/zod-validation/prefixMessage.js deleted file mode 100644 index 3016dcba..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/prefixMessage.js +++ /dev/null @@ -1,13 +0,0 @@ -import { PREFIX } from "./config"; -export function prefixMessage(message, prefix, prefixSeparator) { - if (prefix !== null) { - if (message.length > 0) { - return [prefix, message].join(prefixSeparator); - } - return prefix; - } - if (message.length > 0) { - return message; - } - return PREFIX; -} diff --git a/packages/astro-ghostcms/src/utils/zod-validation/toValidationError.js b/packages/astro-ghostcms/src/utils/zod-validation/toValidationError.js deleted file mode 100644 index ea7e74f6..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/toValidationError.js +++ /dev/null @@ -1,14 +0,0 @@ -import * as zod from "zod"; -import { ValidationError } from "./ValidationError"; -import { fromZodError } from "./fromZodError"; -export const toValidationError = - (options = {}) => - (err) => { - if (err instanceof zod.ZodError) { - return fromZodError(err, options); - } - if (err instanceof Error) { - return new ValidationError(err.message, { cause: err }); - } - return new ValidationError("Unknown error"); - }; diff --git a/packages/astro-ghostcms/src/utils/zod-validation/utils/NonEmptyArray.js b/packages/astro-ghostcms/src/utils/zod-validation/utils/NonEmptyArray.js deleted file mode 100644 index 9fd10456..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/utils/NonEmptyArray.js +++ /dev/null @@ -1,3 +0,0 @@ -export function isNonEmptyArray(value) { - return value.length !== 0; -} diff --git a/packages/astro-ghostcms/src/utils/zod-validation/utils/joinPath.js b/packages/astro-ghostcms/src/utils/zod-validation/utils/joinPath.js deleted file mode 100644 index 86cef5ef..00000000 --- a/packages/astro-ghostcms/src/utils/zod-validation/utils/joinPath.js +++ /dev/null @@ -1,22 +0,0 @@ -const identifierRegex = /[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*/u; -export function joinPath(path) { - if (path.length === 1) { - return path[0].toString(); - } - return path.reduce((acc, item) => { - if (typeof item === "number") { - return `${acc}[${item.toString()}]`; - } - if (item.includes('"')) { - return `${acc}["${escapeQuotes(item)}"]`; - } - if (!identifierRegex.test(item)) { - return `${acc}["${item}"]`; - } - const separator = acc.length === 0 ? "" : "."; - return acc + separator + item; - }, ""); -} -function escapeQuotes(str) { - return str.replace(/"/g, '\\"'); -} diff --git a/packages/astro-ghostcms/tsconfig.json b/packages/astro-ghostcms/tsconfig.json new file mode 100644 index 00000000..0ce5c9fc --- /dev/null +++ b/packages/astro-ghostcms/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "astro/tsconfigs/strictest", + "compilerOptions": { + "jsx": "preserve" + } + } \ No newline at end of file diff --git a/packages/astro-ghostcms/src/integrations/virtual.d.ts b/packages/astro-ghostcms/virtual-config.d.ts similarity index 57% rename from packages/astro-ghostcms/src/integrations/virtual.d.ts rename to packages/astro-ghostcms/virtual-config.d.ts index 45a64ba9..bfef5ddb 100644 --- a/packages/astro-ghostcms/src/integrations/virtual.d.ts +++ b/packages/astro-ghostcms/virtual-config.d.ts @@ -1,4 +1,4 @@ declare module "virtual:@matthiesenxyz/astro-ghostcms/config" { - const Config: import("../schemas/index").GhostUserConfig; + const Config: import("./src/schemas/userconfig").GhostUserConfig; export default Config; } diff --git a/packages/astro-ghostcms/vitest.config.ts b/packages/astro-ghostcms/vitest.config.ts index deec7256..6a723a68 100644 --- a/packages/astro-ghostcms/vitest.config.ts +++ b/packages/astro-ghostcms/vitest.config.ts @@ -1,11 +1,8 @@ /// /// - -import tsconfigPaths from "vite-tsconfig-paths"; import { defineProject } from "vitest/config"; export default defineProject({ - plugins: [tsconfigPaths()], test: { globals: true, include: ["./**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], diff --git a/packages/create-astro-ghostcms/package.json b/packages/create-astro-ghostcms/package.json index 02538d4d..0e125b70 100644 --- a/packages/create-astro-ghostcms/package.json +++ b/packages/create-astro-ghostcms/package.json @@ -46,17 +46,7 @@ "@types/gunzip-maybe": "^1.4.0", "@types/node": "^20.11.19", "@types/tar-fs": "^2.0.1", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-import-resolver-typescript": "^3.5.3", - "prettier": "^3.2.5", - "typescript": "^5.3.3", - "vitest": "^1.3.0", - "vite": "^5.1.3", - "vite-tsconfig-paths": "^4.2.2" + "typescript": "^5.3.3" }, "publishConfig": { "access": "public" diff --git a/packages/create-astro-ghostcms/src/templates/basic/package.json b/packages/create-astro-ghostcms/src/templates/basic/package.json index 9882e8fb..155d14f8 100644 --- a/packages/create-astro-ghostcms/src/templates/basic/package.json +++ b/packages/create-astro-ghostcms/src/templates/basic/package.json @@ -10,9 +10,9 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.2.8", - "@matthiesenxyz/astro-ghostcms": "^3.1.5", - "@matthiesenxyz/astro-ghostcms-theme-default": "^0.1.5" + "astro": "^4.4.0", + "@matthiesenxyz/astro-ghostcms": "^3.3.0", + "@matthiesenxyz/astro-ghostcms-theme-default": "^0.1.13" }, "devDependencies": { "@astrojs/check": "^0.4.1", diff --git a/packages/create-astro-ghostcms/src/templates/brutal/astro.config.mjs b/packages/create-astro-ghostcms/src/templates/brutal/astro.config.mjs index 7eeba2bd..f82f45a0 100644 --- a/packages/create-astro-ghostcms/src/templates/brutal/astro.config.mjs +++ b/packages/create-astro-ghostcms/src/templates/brutal/astro.config.mjs @@ -1,16 +1,18 @@ +import astroGhostCMS from "@matthiesenxyz/astro-ghostcms"; import { defineConfig } from "astro/config"; -import ghostcms from "@matthiesenxyz/astro-ghostcms"; -import UnoCSS from 'unocss/astro'; +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", + astroGhostCMS({ ghostURL: "https://ghostdemo.matthiesen.xyz", - }) + ThemeProvider: { + theme: "@matthiesenxyz/astro-ghostcms-brutal", + }, + }), ], }); diff --git a/packages/create-astro-ghostcms/src/templates/brutal/package.json b/packages/create-astro-ghostcms/src/templates/brutal/package.json index 3f35d750..f23c6966 100644 --- a/packages/create-astro-ghostcms/src/templates/brutal/package.json +++ b/packages/create-astro-ghostcms/src/templates/brutal/package.json @@ -10,9 +10,9 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.3.2", - "@matthiesenxyz/astro-ghostcms": "^3.2.2", - "@matthiesenxyz/astro-ghostcms-brutalbyelian": "^0.0.5", + "astro": "^4.4.0", + "@matthiesenxyz/astro-ghostcms": "^3.3.0", + "@matthiesenxyz/astro-ghostcms-brutalbyelian": "^0.0.11", "@unocss/astro": "^0.57.7" }, "devDependencies": { diff --git a/packages/create-astro-ghostcms/src/templates/brutal/src/env.d.ts b/packages/create-astro-ghostcms/src/templates/brutal/src/env.d.ts index e8c8b171..b05f2dcb 100644 --- a/packages/create-astro-ghostcms/src/templates/brutal/src/env.d.ts +++ b/packages/create-astro-ghostcms/src/templates/brutal/src/env.d.ts @@ -1,9 +1,9 @@ /// interface ImportMetaEnv { - readonly CONTENT_API_KEY: string - readonly CONTENT_API_URL: string + readonly CONTENT_API_KEY: string; + readonly CONTENT_API_URL: string; } interface ImportMeta { - readonly env: ImportMetaEnv; -} \ No newline at end of file + readonly env: ImportMetaEnv; +} diff --git a/packages/create-astro-ghostcms/src/templates/brutal/uno.config.ts b/packages/create-astro-ghostcms/src/templates/brutal/uno.config.ts index d056c836..e3f5c156 100644 --- a/packages/create-astro-ghostcms/src/templates/brutal/uno.config.ts +++ b/packages/create-astro-ghostcms/src/templates/brutal/uno.config.ts @@ -1,6 +1,6 @@ -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() ], + presets: [brutalTheme()], }); diff --git a/packages/create-astro-ghostcms/src/templates/catppuccin/astro.config.mjs b/packages/create-astro-ghostcms/src/templates/catppuccin/astro.config.mjs index ebb71227..720c7e04 100644 --- a/packages/create-astro-ghostcms/src/templates/catppuccin/astro.config.mjs +++ b/packages/create-astro-ghostcms/src/templates/catppuccin/astro.config.mjs @@ -1,5 +1,5 @@ import tailwind from "@astrojs/tailwind"; -import ghostcms from "@matthiesenxyz/astro-ghostcms"; +import astroGhostCMS from "@matthiesenxyz/astro-ghostcms"; import { defineConfig } from "astro/config"; // https://astro.build/config @@ -7,9 +7,11 @@ export default defineConfig({ site: "https://example.xyz/", integrations: [ tailwind(), - ghostcms({ - theme: "@matthiesenxyz/astro-ghostcms-catppuccin", + astroGhostCMS({ ghostURL: "https://ghostdemo.matthiesen.xyz", + ThemeProvider: { + theme: "@matthiesenxyz/astro-ghostcms-catppuccin", + }, }), ], }); diff --git a/packages/create-astro-ghostcms/src/templates/catppuccin/package.json b/packages/create-astro-ghostcms/src/templates/catppuccin/package.json index a4d190a7..622d693c 100644 --- a/packages/create-astro-ghostcms/src/templates/catppuccin/package.json +++ b/packages/create-astro-ghostcms/src/templates/catppuccin/package.json @@ -10,9 +10,9 @@ "astro": "astro" }, "dependencies": { - "astro": "^4.2.8", - "@matthiesenxyz/astro-ghostcms": "^3.1.8", - "@matthiesenxyz/astro-ghostcms-catppuccin": "^0.0.3", + "astro": "^4.4.0", + "@matthiesenxyz/astro-ghostcms": "^3.3.0", + "@matthiesenxyz/astro-ghostcms-catppuccin": "^0.0.8", "@astrojs/tailwind": "^5.1.0", "tailwindcss": "^3.3.5" }, diff --git a/packages/create-astro-ghostcms/src/templates/starterkit/astro.config.mjs b/packages/create-astro-ghostcms/src/templates/starterkit/astro.config.mjs index 49d63c42..3c99e570 100644 --- a/packages/create-astro-ghostcms/src/templates/starterkit/astro.config.mjs +++ b/packages/create-astro-ghostcms/src/templates/starterkit/astro.config.mjs @@ -9,10 +9,13 @@ export default defineConfig({ // Includes GhostCMS API, @astrojs/rss, @astrojs/sitemap, and astro-robots-txt GhostCMS({ ghostURL: "https://ghostdemo.matthiesen.xyz", - // This Option Disables all default theme injection and allows DIY mode. - disableRouteInjection: true, - // Enable this to disable the extra console logs - disableConsoleOutput: false, + ThemeProvider: { + disableThemeProvider: true, + }, + disableDefault404: true, + enableRSSFeed: false, + enableOGImages: false, + verbose: false, }), ], }); diff --git a/packages/create-astro-ghostcms/src/templates/starterkit/package.json b/packages/create-astro-ghostcms/src/templates/starterkit/package.json index 7684d5b8..382cb805 100644 --- a/packages/create-astro-ghostcms/src/templates/starterkit/package.json +++ b/packages/create-astro-ghostcms/src/templates/starterkit/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@astrojs/check": "^0.4.1", - "@matthiesenxyz/astro-ghostcms": "^3.1.4", - "astro": "^4.2.8", + "@matthiesenxyz/astro-ghostcms": "^3.3.0", + "astro": "^4.4.0", "typescript": "^5.3.3", "astro-font": "^0.0.77" }, diff --git a/packages/starlight-ghostcms/README.md b/packages/starlight-ghostcms/README.md index bcd4ada5..a756b101 100644 --- a/packages/starlight-ghostcms/README.md +++ b/packages/starlight-ghostcms/README.md @@ -48,7 +48,9 @@ import starlightGhostCMS from '@matthiesenxyz/starlight-ghostcms'; export default defineConfig({ integrations: [ starlight({ - plugins: [starlightGhostCMS()], + plugins: [ + starlightGhostCMS() + ], title: 'My Docs', }), ], diff --git a/packages/starlight-ghostcms/index.ts b/packages/starlight-ghostcms/index.ts index 0c018270..1bf51245 100644 --- a/packages/starlight-ghostcms/index.ts +++ b/packages/starlight-ghostcms/index.ts @@ -1,91 +1,117 @@ -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 { + type StarlightGhostConfig, + validateConfig, +} from "./src/schemas/config"; +import { facebook, getSettings, invariant, twitter } from "./src/utils/api"; +import starlightGhostcms from "./src/integrations/starlight-ghostcms"; -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', - hooks: { - setup({ astroConfig, addIntegration, config: starlightConfig, logger, updateConfig: updateStarlightConfig }) { - updateStarlightConfig({ - social: { - ...starlightConfig.social, - rss: `${astroConfig.site}/rss.xml`, - twitter: twitter(settings.twitter?settings.twitter:""), - facebook: facebook(settings.facebook?settings.facebook:""), - }, - components: { - ...starlightConfig.components, - ...overrideStarlightComponent(starlightConfig.components, logger, 'MarkdownContent'), - ...overrideStarlightComponent(starlightConfig.components, logger, 'Sidebar'), - ...overrideStarlightComponent(starlightConfig.components, logger, "SiteTitle"), - } - }) + return { + name: "@matthiesenxyz/starlight-ghostcms-plugin", + hooks: { + setup({ + astroConfig, + addIntegration, + config: starlightConfig, + logger, + updateConfig: updateStarlightConfig, + }) { + // Add the Starlight-GhostCMS integration + addIntegration(starlightGhostcms(config)); + + // Update the Starlight config with the GhostCMS config + updateStarlightConfig({ + social: { + ...starlightConfig.social, + ...overrideRSS(starlightConfig.social, astroConfig.site), + ...overrideTwitter(starlightConfig.social), + ...overrideFacebook(starlightConfig.social), + }, + components: { + ...starlightConfig.components, + ...overrideStarlightComponent( + starlightConfig.components, + logger, + "MarkdownContent", + ), + ...overrideStarlightComponent( + starlightConfig.components, + logger, + "Sidebar", + ), + ...overrideStarlightComponent( + starlightConfig.components, + logger, + "SiteTitle", + ), + }, + }); + }, + }, + }; +} - addIntegration({ - name: '@matthiesenxyz/starlight-ghostcms', - hooks: { - 'astro:config:setup': ({ injectRoute, updateConfig }) => { - injectRoute({ - pattern: '/blog', - entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/index.astro', - prerender: true, - }) - injectRoute({ - pattern: '/blog/[slug]', - entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/[slug].astro', - prerender: true, - }) - injectRoute({ - pattern: '/blog/about', - entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/about.astro', - prerender: true, - }) - injectRoute({ - pattern: '/blog/authors', - entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/authors.astro', - }) - injectRoute({ - pattern: '/rss.xml', - entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/rss.xml.ts' - }) +function overrideRSS( + socials: StarlightUserConfig["social"], + url: string | undefined + ) { + if (socials?.rss) { return {}; } + if (url === undefined) { return undefined; } + return { rss: `${url}/rss.xml` }; +} - updateConfig({ - vite: { - plugins: [vitePluginStarlightGhostConfig(config)], - }, - }) - } - } - }) - } - }, - } +function overrideTwitter( + socials: StarlightUserConfig["social"], + ) { + if (socials?.twitter) { return {}; } + if (settings?.twitter) { + return { twitter: twitter(settings.twitter), } + } + return undefined; +} + +function overrideFacebook( + socials: StarlightUserConfig["social"], + ) { + if (socials?.facebook) { return {}; } + if (settings?.facebook) { + return { facebook: facebook(settings.facebook), } + } + return undefined; } function overrideStarlightComponent( - components: StarlightUserConfig['components'], - logger: AstroIntegrationLogger, - component: keyof NonNullable, - ) { - 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 ;)") - - return {} - } - - return { - [component]: `@matthiesenxyz/starlight-ghostcms/overrides/${component}.astro`, - } - } \ No newline at end of file + components: StarlightUserConfig["components"], + logger: AstroIntegrationLogger, + component: keyof NonNullable, +) { + 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 ;)"); + + return {}; + } + + return { + [component]: `@matthiesenxyz/starlight-ghostcms/overrides/${component}.astro`, + }; +} diff --git a/packages/starlight-ghostcms/package.json b/packages/starlight-ghostcms/package.json index dab3be89..b76493a3 100644 --- a/packages/starlight-ghostcms/package.json +++ b/packages/starlight-ghostcms/package.json @@ -36,21 +36,15 @@ "types": "index.ts", "files": [ "src", + "CHANGELOG.md", "index.ts", - "tsconfig.json", - "types.d.ts" + "LICENSE", + "package.json", + "README.md" ], "exports": { ".": "./index.ts", - "./overrides/MarkdownContent.astro": "./src/overrides/MarkdownContent.astro", - "./overrides/Sidebar.astro": "./src/overrides/Sidebar.astro", - "./overrides/SiteTitle.astro": "./src/overrides/SiteTitle.astro", - "./routes/index.astro": "./src/routes/index.astro", - "./routes/about.astro": "./src/routes/about.astro", - "./routes/authors.astro": "./src/routes/authors.astro", - "./routes/[slug].astro": "./src/routes/[slug].astro", - "./routes/rss.xml.ts": "./src/routes/rss.xml.ts", - "./schema": "./src/schemas/config.ts" + "./overrides/*": "./src/overrides/*" }, "scripts": { "test": "vitest run", @@ -60,19 +54,20 @@ }, "devDependencies": { "@astrojs/starlight": "^0.19.0", - "@ts-ghost/tsconfig": "workspace:*", - "astro": "^4.4.0", - "vitest": "^1.2.2", + "astro": "^4.4.1", + "vitest": "^1.3.1", "vitest-fetch-mock": "^0.2.2" }, "dependencies": { "@astrojs/rss": "^4.0.5", - "@ts-ghost/core-api": "^5.1.2", - "vite": "^5.1.2", - "vite-tsconfig-paths": "^4.2.2" + "@ts-ghost/core-api": "^6.0.0", + "@ts-ghost/content-api": "^4.0.12", + "astro-integration-kit": "^0.5.1", + "vite": "^5.1.4", + "vite-tsconfig-paths": "^4.3.1" }, "peerDependencies": { "@astrojs/starlight": ">=0.19.0", - "astro": ">=4.3.7" + "astro": ">=4.4.1" } } diff --git a/packages/starlight-ghostcms/src/integrations/starlight-ghostcms.ts b/packages/starlight-ghostcms/src/integrations/starlight-ghostcms.ts new file mode 100644 index 00000000..e547be7b --- /dev/null +++ b/packages/starlight-ghostcms/src/integrations/starlight-ghostcms.ts @@ -0,0 +1,52 @@ +import { + createResolver, + defineIntegration, +} from "astro-integration-kit"; +import { corePlugins } from "astro-integration-kit/plugins"; +import { z } from "astro/zod"; +import { type StarlightGhostConfig } from "../schemas/config"; + +export default defineIntegration({ + name: "@matthiesenxyz/starlight-ghostcms", + optionsSchema: z.custom(), + plugins: [...corePlugins], + setup({ options }) { + const { resolve } = createResolver(import.meta.url); + + return { + "astro:config:setup": ({ + watchIntegration, + addVirtualImports, + injectRoute + }) => { + watchIntegration(resolve()); + + addVirtualImports({ + 'virtual:starlight-ghostcms/config': `export default ${JSON.stringify(options)}`, + }); + + const makeRoute = (endpoint: string, entrypoint: string) => { + injectRoute({ + pattern: `/${endpoint}`, + entrypoint: resolve(`../routes/${entrypoint}`), + prerender: true, + }); + }; + const sanitisedRoute = options.route + .replace(/^\//, '') + .replace(/\/$/, ''); + + makeRoute(`${sanitisedRoute}`, + "index.astro"); + makeRoute(`${sanitisedRoute}/[slug]`, + "[slug].astro"); + makeRoute(`${sanitisedRoute}/about`, + "about.astro"); + makeRoute(`${sanitisedRoute}/authors`, + "authors.astro"); + makeRoute("rss.xml", + "rss.xml.ts"); + } + } + } +}) \ No newline at end of file diff --git a/packages/starlight-ghostcms/src/integrations/vite.ts b/packages/starlight-ghostcms/src/integrations/vite.ts deleted file mode 100644 index 7ccb3d43..00000000 --- a/packages/starlight-ghostcms/src/integrations/vite.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { ViteUserConfig } from 'astro' - -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)}` - - return { - name: 'vite-plugin-starlight-ghost-config', - load(id) { - return id === resolvedModuleId ? moduleContent : undefined - }, - resolveId(id) { - return id === moduleId ? resolvedModuleId : undefined - }, - } -} - -type VitePlugin = NonNullable[number] diff --git a/packages/starlight-ghostcms/src/overrides/MarkdownContent.astro b/packages/starlight-ghostcms/src/overrides/MarkdownContent.astro index 80c0151b..b9df9e05 100644 --- a/packages/starlight-ghostcms/src/overrides/MarkdownContent.astro +++ b/packages/starlight-ghostcms/src/overrides/MarkdownContent.astro @@ -1,22 +1,27 @@ --- import StarlightMarkdownContent from '@astrojs/starlight/components/MarkdownContent.astro' import type { Props } from '@astrojs/starlight/props' - +import config from 'virtual:starlight-ghostcms/config' import { isAnyBlogPostPage } from '../utils/page' import Metadata from '../components/Metadata.astro' import type { Post } from '../schemas/posts' +export function checkpath(path: string){ + return path.split('/').includes(config.route) ? true : false +} + +const isBlog = checkpath(Astro.url.pathname) const isBlogPost = isAnyBlogPostPage(Astro.props.slug) let blogEntry: Post | undefined = undefined --- - {isBlogPost && blogEntry ? : null} + {isBlog && blogEntry ? : null} { - isBlogPost && blogEntry ? ( + isBlog && blogEntry ? ( ) : null diff --git a/packages/starlight-ghostcms/src/overrides/Sidebar.astro b/packages/starlight-ghostcms/src/overrides/Sidebar.astro index a75b80b9..885426c2 100644 --- a/packages/starlight-ghostcms/src/overrides/Sidebar.astro +++ b/packages/starlight-ghostcms/src/overrides/Sidebar.astro @@ -1,9 +1,9 @@ --- import StarlightSidebar from '@astrojs/starlight/components/Sidebar.astro' import type { Props } from '@astrojs/starlight/props' -import config from 'virtual:starlight-ghost-config' +import config from 'virtual:starlight-ghostcms/config' import { isBlogPostPage, isBlogRoot } from '../utils/page' -import { getAllPages, getAllPosts, getSluggedPage } from '../utils/api/api-functions.js' +import { getAllPages, getAllPosts, getSluggedPage } from '../utils/api' import type { SidebarEntry } from './sidebartypes' export async function getRecentBlogEntries(){ @@ -17,19 +17,17 @@ export async function getBlogPageEntries(){ } export function checkpath(path: string){ - if ( path.slice(0, 5) === "/blog" ){ - return true - } else { return false } + return path.split('/').includes(config.route) ? true : false } export function isAbout(path: string){ - if ( path === "/blog/about" ){ + if ( path === `/${config.route}/about` ){ return true } else { return false } } export function isAuthors(path: string){ - if ( path === "/blog/authors" ){ + if ( path === `/${config.route}/authors` ){ return true } else { return false } } @@ -38,7 +36,7 @@ const recentEntries = isBlog ? await getRecentBlogEntries() : [] const aboutPage = await getSluggedPage("about"); const AboutEntry:SidebarEntry = { attrs: {}, badge: undefined, - href: '/blog/about', + href: `/${config.route}/about`, isCurrent: isAbout(Astro.url.pathname), type: 'link', label: aboutPage?.post?.title @@ -55,7 +53,7 @@ const blogSidebar: Props['sidebar'] = isBlog { attrs: {}, badge: undefined, - href: '/blog/authors', + href: `/${config.route}/authors`, isCurrent: isAuthors(Astro.url.pathname), label: 'Our Authors', type: 'link', @@ -63,7 +61,7 @@ const blogSidebar: Props['sidebar'] = isBlog { attrs: {}, badge: undefined, - href: '/blog', + href: `/${config.route}`, isCurrent: isBlogRoot(Astro.props.slug), label: 'All posts', type: 'link', @@ -74,8 +72,8 @@ const blogSidebar: Props['sidebar'] = isBlog entries: recentEntries.map((blogEntry) => ({ attrs: {}, badge: blogEntry.featured?({text: "⭐", variant: "note"}):undefined, - href: `/blog/${blogEntry.slug}`, - isCurrent: isBlogPostPage(Astro.props.slug, `blog/${blogEntry.slug}`), + href: `/${config.route}/${blogEntry.slug}`, + isCurrent: isBlogPostPage(Astro.props.slug, `${config.route}/${blogEntry.slug}`), label: blogEntry.title, type: 'link', })), @@ -89,7 +87,7 @@ const blogSidebar: Props['sidebar'] = isBlog { !isBlog && (
- Blog + Blog
) } diff --git a/packages/starlight-ghostcms/src/overrides/SiteTitle.astro b/packages/starlight-ghostcms/src/overrides/SiteTitle.astro index 328b4874..5093fd36 100644 --- a/packages/starlight-ghostcms/src/overrides/SiteTitle.astro +++ b/packages/starlight-ghostcms/src/overrides/SiteTitle.astro @@ -1,11 +1,12 @@ --- import type { Props } from "@astrojs/starlight/props"; import AstrolightSiteTitle from "@astrojs/starlight/components/SiteTitle.astro"; +import config from 'virtual:starlight-ghostcms/config' ---