import { createResolver, defineIntegration } from "astro-integration-kit"; import { addDtsPlugin, corePlugins } from "astro-integration-kit/plugins"; import tailwindcss from "@tailwindcss/vite"; import { optionsSchema } from "./schemas/user-config"; import c from "picocolors"; import { AstroError } from "astro/errors"; import { readFileSync } from "node:fs"; import { squooshImageService } from "astro/config"; /** * Astro-Hashnode Integration */ export default defineIntegration({ name: "@matthiesenxyz/astro-hashnode", optionsSchema, plugins: [ ...corePlugins, addDtsPlugin ], setup({ options }) { type outputType = "static" | "hybrid" | "server"; return { "astro:config:setup": ({ watchIntegration, addVitePlugin, addVirtualImports, addDts, injectScript, injectRoute, updateConfig, config, logger, }) => { logger.info("Initializing...") // Create Resolvers const { resolve } = createResolver(import.meta.url); const { resolve: rootResolve} = createResolver(config.root.pathname) // Watch Integration for changes in DEV watchIntegration(resolve()) const HashLogger = logger.fork(c.bold(c.blue("Astro-Hashnode"))); const hashLogNoVerbose = (message:string) => { HashLogger.info(c.magenta(message)) } const hashLog = (message:string) => { if (options.verbose) { HashLogger.info(c.magenta(message)) } } const hashError = (message:string) => { HashLogger.error(c.magenta(message)) throw new AstroError(message) } // Check if output is static or hybrid const checkIfStatic = (output: outputType) => { if (output === "static") { return true } if (output === "hybrid") { return true } return false } hashLogNoVerbose("Setting up Astro-Hashnode Integration") // Check for Hashnode URL if (!options.hashnodeURL) { hashError("You must provide a hashnodeURL in your astro.config.mjs file. Use the hashnodeURL from your hashnode blog. It should look something like this: 'yourblog.hashnode.dev'") } hashLog(`Using Hashnode URL: ${options.hashnodeURL}`) hashLog("Setting up Virtual Imports and Layout Component") // Setup Layout Component let layoutComponentPath: string; if (options.layoutComponent) { layoutComponentPath = rootResolve(options.layoutComponent) hashLog('Using user defined layout component') } else { layoutComponentPath = resolve('./layouts/Layout.astro') } addVirtualImports({ 'virtual:astro-hashnode/config': `export default ${JSON.stringify(options) }`, 'virtual:astro-hashnode/components': `export { default as Layout } from "${layoutComponentPath}";`, }) addDts({ name: 'astro-hashnode', content: readFileSync(resolve("./definitions/astro-hashnode.d.ts"), "utf-8"), }) // Add & Setup Tailwind CSS hashLog("Setting up 'Tailwind CSS v4' Integration") const twplugin = tailwindcss(); for (const twp of twplugin) { addVitePlugin(twp); } injectScript( "page-ssr", `import "${resolve("./styles/tailwind.css")}";` ); // Update Astro Config updateConfig({ image: { service: squooshImageService(), }, vite: { css: { transformer: "lightningcss" }, } }) // Add Page Routes hashLog("Setting up Page Routes") if (options.landingPage) { injectRoute({ pattern: config.base, entrypoint: resolve("./pages/index.astro"), prerender: true, }); } injectRoute({ pattern: `${config.base}blog`, entrypoint: resolve("./pages/blog/index.astro"), }) injectRoute({ pattern: `${config.base}blog/about`, entrypoint: resolve("./pages/blog/about.astro"), }) injectRoute({ pattern: `${config.base}blog/tags/[tag]`, entrypoint: resolve("./pages/blog/tags/[tag].astro"), prerender: true, }) // Add Blog Post Routes based on output type if ( checkIfStatic(config.output) ) { // Static & Hybrid Routes injectRoute({ pattern: `${config.base}blog/[slug]`, entrypoint: resolve("./pages/blog/[slug].astro"), prerender: true, }) } else { // Server Routes injectRoute({ pattern: `${config.base}blog/[slug]`, entrypoint: resolve("./pages/ssr-pages/[slug].astro"), }) } }, "astro:config:done": ({ logger }) => { const HashLogger = logger.fork(c.bold(c.blue("Astro-Hashnode"))); const hashLog = (message:string) => { HashLogger.info(c.green(message)) } hashLog("Astro-Hashnode Integration Setup Complete") } } } }) export { type AstroHashnodeLayoutProps } from "./proptypes/layouttypes"