easy-web-cms-adapters
Installation
Section titled “Installation”pnpm add @easy-web/cms-adaptersOverview
Section titled “Overview”@easy-web/cms-adapters integrates Decap CMS into the easy-web ecosystem. It provides three things:
AdminPageAstro component — a standalone page that loads Decap CMS from CDN. It deliberately does not use the site’s main Astro layout so it cannot interfere with the MSAL-based authentication on the rest of the site.- Config generator —
generateDecapConfigStringandgenerateDecapConfigproduce theconfig.ymlthat Decap requires, pre-wired for the Azure DevOps backend and Entra ID OAuth. - Frontmatter TypeScript types —
BlogFrontmatter,PageFrontmatter,SiteConfig, and more, for type-safe Astro content collections.
See ADR 0006 for the adapter-pattern design rationale.
AdminPage component
Section titled “AdminPage component”AdminPage is a standalone Astro component that renders a complete HTML admin interface. Because it deliberately avoids the site layout, it must be placed at a route that uses no shared layout (typically /admin).
Import: import AdminPage from '@easy-web/cms-adapters/components/AdminPage.astro'
---// No layout — AdminPage renders the full HTML document itselfimport AdminPage from '@easy-web/cms-adapters/components/AdminPage.astro';---<AdminPage />The component loads Decap CMS from unpkg.com/decap-cms@3.14.0 and expects public/admin/config.yml to be present. Generate that file using the config utilities below.
Config generator
Section titled “Config generator”generateDecapConfigString
Section titled “generateDecapConfigString”Generates a Decap CMS config.yml string for the Azure DevOps backend, pre-configured for blog and pages collections with per-locale support.
Signature:
function generateDecapConfigString(options: DecapConfigOptions): stringinterface DecapConfigOptions { tenantId: string; // Entra tenant ID appId: string; // Entra app registration client ID for the CMS adoOrg: string; // Azure DevOps organisation slug adoProject: string; // Azure DevOps project name adoRepo: string; // Repository name branch?: string; // Target branch (default: 'main') locales?: string[]; // Locales to create blog collections for (default: ['de', 'en'])}import { generateDecapConfigString } from '@easy-web/cms-adapters';
const yaml = generateDecapConfigString({ tenantId: process.env.ENTRA_TENANT_ID, appId: process.env.ENTRA_CMS_APP_ID, adoOrg: 'my-org', adoProject: 'my-project', adoRepo: 'my-repo', branch: 'main', locales: ['de', 'en'],});
console.log(yaml); // Full config.yml content ready to write to public/admin/config.ymlgenerateDecapConfig
Section titled “generateDecapConfig”Async variant of generateDecapConfigString that also writes the generated config to a file path. Returns a result object indicating success or failure. Will not overwrite an existing file.
Signature:
async function generateDecapConfig(options: DecapConfigWriteOptions): Promise<DecapConfigWriteResult>interface DecapConfigWriteOptions extends DecapConfigOptions { outputPath: string; // Absolute or relative path to write config.yml}
interface DecapConfigWriteResult { success: boolean; content: string; // The generated YAML string error?: string; // Present only when success === false}import { generateDecapConfig } from '@easy-web/cms-adapters';import path from 'node:path';
const result = await generateDecapConfig({ tenantId: process.env.ENTRA_TENANT_ID!, appId: process.env.ENTRA_CMS_APP_ID!, adoOrg: 'my-org', adoProject: 'my-project', adoRepo: 'my-repo', outputPath: path.join(process.cwd(), 'public', 'admin', 'config.yml'),});
if (!result.success) { console.error(result.error);} else { console.log('config.yml written successfully');}DecapConfigOptions
Section titled “DecapConfigOptions”interface DecapConfigOptions { tenantId: string; appId: string; adoOrg: string; adoProject: string; adoRepo: string; branch?: string; // default: 'main' locales?: string[]; // default: ['de', 'en']}DecapConfigWriteOptions
Section titled “DecapConfigWriteOptions”Extends DecapConfigOptions with outputPath: string.
DecapConfigWriteResult
Section titled “DecapConfigWriteResult”interface DecapConfigWriteResult { success: boolean; content: string; error?: string;}Frontmatter types
Section titled “Frontmatter types”These TypeScript types match the Decap CMS collection schemas generated by generateDecapConfigString. Import them in your Astro content collection config for type-safe frontmatter.
import type { BlogFrontmatter, PageFrontmatter, SiteConfig,} from '@easy-web/cms-adapters';BlogFrontmatter
Section titled “BlogFrontmatter”Frontmatter shape for entries in src/content/blog/[locale]/.
// Fields include: title, description, pubDate, draft, locale, translationKey, heroImage?import type { BlogFrontmatter } from '@easy-web/cms-adapters';PageFrontmatter
Section titled “PageFrontmatter”Frontmatter shape for entries in src/content/pages/.
// Fields include: title, description, locale, translationKeyimport type { PageFrontmatter } from '@easy-web/cms-adapters';SiteConfig
Section titled “SiteConfig”Type for site-wide configuration data managed in Decap (e.g. src/content/data/company.json).
// Fields include: companyName, defaultTitle, urlimport type { SiteConfig } from '@easy-web/cms-adapters';Additional types
Section titled “Additional types”The package also exports: EventFrontmatter, PersonFrontmatter, LinkItem, and NavigationConfig — imported from the same entry point.
Architecture note
Section titled “Architecture note”Decap CMS uses an OAuth hash-fragment redirect flow. Mounting it inside the site’s main Astro layout would allow the hash fragment to collide with MSAL’s own redirect handler. The AdminPage component isolates Decap to its own page to prevent this.
For Entra ID app registration guidance for the CMS backend, see docs/entra-cms-setup.md in your instance repo.