Skip to content

easy-web-cms-adapters

Terminal window
pnpm add @easy-web/cms-adapters

@easy-web/cms-adapters integrates Decap CMS into the easy-web ecosystem. It provides three things:

  1. AdminPage Astro 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.
  2. Config generator — generateDecapConfigString and generateDecapConfig produce the config.yml that Decap requires, pre-wired for the Azure DevOps backend and Entra ID OAuth.
  3. Frontmatter TypeScript types — BlogFrontmatter, PageFrontmatter, SiteConfig, and more, for type-safe Astro content collections.

See ADR 0006 for the adapter-pattern design rationale.

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'

src/pages/admin.astro
---
// No layout — AdminPage renders the full HTML document itself
import 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.

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): string
interface 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.yml

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');
}
interface DecapConfigOptions {
tenantId: string;
appId: string;
adoOrg: string;
adoProject: string;
adoRepo: string;
branch?: string; // default: 'main'
locales?: string[]; // default: ['de', 'en']
}

Extends DecapConfigOptions with outputPath: string.

interface DecapConfigWriteResult {
success: boolean;
content: string;
error?: string;
}

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';

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';

Frontmatter shape for entries in src/content/pages/.

// Fields include: title, description, locale, translationKey
import type { PageFrontmatter } from '@easy-web/cms-adapters';

Type for site-wide configuration data managed in Decap (e.g. src/content/data/company.json).

// Fields include: companyName, defaultTitle, url
import type { SiteConfig } from '@easy-web/cms-adapters';

The package also exports: EventFrontmatter, PersonFrontmatter, LinkItem, and NavigationConfig — imported from the same entry point.

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.