feat(cli): add Astro template to FireCMS CLI#765
Conversation
Add a new "astro" template option to the `firecms init` command, allowing users to scaffold a FireCMS project with Astro SSG/SSR and blog support. - Add `template_astro` to CLI templates with templatized Firebase config, cleaned monorepo aliases, and npm package dependencies - Update [init.ts](cci:7://file:///Users/marian/Code/firecms/firecms/packages/cli/src/commands/init.ts:0:0-0:0): add `--astro` flag, interactive prompt option, template folder mapping, post-creation instructions, and file replacement logic - Update CLI build script to clean template_astro node_modules - Add Astro example project with CMS dashboard, public blog pages, and FireCMS-branded dark theme styling
There was a problem hiding this comment.
Pull request overview
This PR adds a new "astro" template to the FireCMS CLI, enabling users to scaffold FireCMS projects with Astro SSG/SSR support and a public blog feature. The implementation includes both a CLI template (for distribution to users) and an example project (for monorepo development/testing).
Changes:
- Added
--astroflag and template option to CLI init command with appropriate prompts and post-creation instructions - Created
template_astrowith Astro configuration, FireCMS dashboard setup, blog components, and Firebase integration - Added parallel
example_astroin examples directory for monorepo development - Updated lerna.json to include the new example project in the monorepo workspace
Reviewed changes
Copilot reviewed 50 out of 52 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/cli/src/commands/init.ts | Added astro template support with flag, prompt, folder mapping, and file replacement logic |
| packages/cli/README.md | Added documentation for the new --astro CLI flag |
| packages/cli/templates/template_astro/* | New template directory with Astro app structure, FireCMS integration, and blog support |
| examples/example_astro/* | Parallel example project for monorepo development with proper monorepo path mappings |
| lerna.json | Added examples/example_astro to workspace packages |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const handleSubmit = (email: string) => { | ||
| const url = "https://api.firecms.co/notifications/newsletter"; | ||
| fetch(url, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json" | ||
| }, | ||
| body: JSON.stringify({ | ||
| email_address: email, | ||
| source: "demo" | ||
| }) | ||
| }).then((res) => { | ||
| console.log("newsletter response", res); | ||
| }); | ||
| } |
There was a problem hiding this comment.
The newsletter submission lacks error handling. If the fetch fails or the response is not ok, the error is silently ignored. Add proper error handling with a catch block and check response.ok to handle failure cases appropriately.
| "examples/example_pro" | ||
| ], |
There was a problem hiding this comment.
The trailing comma on line 32 after the last array element (followed by the closing bracket on line 33) is unnecessary and may cause issues with some JSON parsers that don't support trailing commas. While it's valid in JSON5 and modern JavaScript, standard JSON does not allow trailing commas.
| export const firebaseConfig = { | ||
| apiKey: "YOUR_API_KEY", | ||
| authDomain: "[REPLACE_WITH_PROJECT_ID].firebaseapp.com", | ||
| projectId: "[REPLACE_WITH_PROJECT_ID]", | ||
| storageBucket: "[REPLACE_WITH_PROJECT_ID].appspot.com", | ||
| messagingSenderId: "YOUR_MESSAGING_SENDER_ID", | ||
| appId: "YOUR_APP_ID" | ||
| }; |
There was a problem hiding this comment.
The template's Firebase configuration contains placeholder values that need to be replaced by the CLI. However, the firebaseConfig should include a check in App.tsx (already present at line 47-49) to ensure users are warned if they forget to configure it. Consider also adding a more descriptive comment in this file explaining that these values will be automatically replaced during CLI initialization.
| <a | ||
| href="/cms" | ||
| class="glow-blue" | ||
| style="display:inline-flex;align-items:center;gap:0.5rem;padding:0.75rem 1.5rem;border-radius:0.5rem;background:#0070f4;color:#ffffff;font-weight:600;text-decoration:none;transition:all 0.2s;" | ||
| onmouseenter="this.style.transform='scale(1.05)'" | ||
| onmouseleave="this.style.transform='scale(1)'" | ||
| > | ||
| Open CMS → | ||
| </a> | ||
| <a | ||
| href="/blog" | ||
| style="display:inline-flex;align-items:center;gap:0.5rem;padding:0.75rem 1.5rem;border-radius:0.5rem;border:1px solid #454552;background:rgba(23, 23, 26, 0.5);color:#b7b7bf;font-weight:500;text-decoration:none;transition:all 0.2s;" | ||
| onmouseenter="this.style.borderColor='#0070f4';this.style.color='#0070f4'" | ||
| onmouseleave="this.style.borderColor='#454552';this.style.color='#b7b7bf'" | ||
| > |
There was a problem hiding this comment.
The inline event handlers use string-based style manipulation (e.g., onmouseenter="this.style.transform='scale(1.05)'"). This approach is less maintainable and harder to test than using CSS classes with hover states. Consider using Tailwind's hover utilities or CSS classes instead of inline JavaScript for hover effects.
| @source "../../packages/**/src/**/*.{js,ts,jsx,tsx}"; | ||
| @source "../../node_modules/@firecms/**/src/**/*.{js,ts,jsx,tsx}"; |
There was a problem hiding this comment.
The @source directives reference monorepo-specific paths (lines 10-11) that won't exist in user projects. Lines 10-11 should be removed: "../../packages//src//.{js,ts,jsx,tsx}" and "../../node_modules/@firecms//src//.{js,ts,jsx,tsx}". Only keep the local source references (lines 8-9).
| import "@fontsource/poppins"; | ||
| import "@fontsource/playfair-display"; | ||
| import "@fontsource/jetbrains-mono"; |
There was a problem hiding this comment.
These font imports (Poppins, Playfair Display) are not used in the CMS pages. The design system primarily uses Rubik (as seen in index.astro and blog pages). These unused imports should be removed to improve bundle size and reduce unnecessary network requests.
| function renderMarkdown(md: string): string { | ||
| let html = md | ||
| .replace(/```([\s\S]*?)```/g, '<pre style="background:rgb(23,23,26);border-radius:0.75rem;padding:1rem;margin:1rem 0;overflow-x:auto;font-size:0.875rem;font-family:monospace;color:#b7b7bf"><code>$1</code></pre>') | ||
| .replace(/`([^`]+)`/g, '<code style="background:rgb(23,23,26);padding:0.125rem 0.375rem;border-radius:0.25rem;color:#0070f4;font-size:0.875rem">$1</code>') | ||
| .replace(/^### (.+)$/gm, '<h3 style="font-size:1.25rem;font-weight:600;color:#ffffff;margin:2rem 0 0.75rem">$1</h3>') | ||
| .replace(/^## (.+)$/gm, '<h2 style="font-size:1.5rem;font-weight:700;color:#ffffff;margin:2.5rem 0 1rem">$1</h2>') | ||
| .replace(/^# (.+)$/gm, '<h1 style="font-size:2rem;font-weight:700;color:#ffffff;margin:2.5rem 0 1rem">$1</h1>') | ||
| .replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" style="border-radius:0.75rem;margin:1.5rem 0;max-width:100%" loading="lazy" />') | ||
| .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" style="color:#0070f4" target="_blank" rel="noopener noreferrer">$1</a>') | ||
| .replace(/\*\*(.+?)\*\*/g, '<strong style="color:#ffffff;font-weight:600">$1</strong>') | ||
| .replace(/\*(.+?)\*/g, '<em>$1</em>') | ||
| .replace(/^[-*] (.+)$/gm, '<li style="margin-left:1rem;color:#b7b7bf;margin-bottom:0.25rem">$1</li>') | ||
| .replace(/^(?!<[hlupai]|<code|<pre|<li|<img)(.+)$/gm, '<p style="color:#b7b7bf;line-height:1.75;margin-bottom:1rem">$1</p>'); | ||
|
|
||
| html = html.replace(/((?:<li[^>]*>.*<\/li>\s*)+)/g, '<ul style="list-style:disc;margin:1rem 0 1rem 1rem">$1</ul>'); | ||
| return html; | ||
| } | ||
|
|
||
| function ContentRenderer({ block }: { block: ResolvedContentBlock }) { | ||
| switch (block.type) { | ||
| case "text": | ||
| return <div dangerouslySetInnerHTML={{ __html: renderMarkdown(block.value || "") }} />; |
There was a problem hiding this comment.
The renderMarkdown function uses dangerouslySetInnerHTML without proper sanitization of user-provided markdown content. While basic markdown is parsed, there's no protection against XSS attacks if malicious HTML is embedded in the markdown. Consider using a proper markdown library with built-in XSS protection (like marked with DOMPurify) or at least sanitize the HTML output before rendering.
| "@firecms/core": [ | ||
| "../../packages/firecms_core/src" | ||
| ], | ||
| "@firecms/firebase": [ | ||
| "../../packages/firebase_firecms/src" | ||
| ], | ||
| "@firecms/ui": [ | ||
| "../../packages/ui/src" | ||
| ], | ||
| "@firecms/editor": [ | ||
| "../../packages/editor/src" | ||
| ], | ||
| "@firecms/data_enhancement": [ | ||
| "../../packages/data_enhancement/src" | ||
| ], | ||
| "@firecms/collection_editor": [ | ||
| "../../packages/collection_editor/src" | ||
| ], | ||
| "@firecms/collection_editor_firebase": [ | ||
| "../../packages/collection_editor_firebase/src" | ||
| ], | ||
| "@firecms/data_import": [ | ||
| "../../packages/data_import/src" | ||
| ], | ||
| "@firecms/data_export": [ | ||
| "../../packages/data_export/src" | ||
| ], | ||
| "@firecms/user_management": [ | ||
| "../../packages/user_management/src" | ||
| ], | ||
| "@firecms/schema_inference": [ | ||
| "../../packages/schema_inference/src" | ||
| ] |
There was a problem hiding this comment.
The tsconfig.json contains monorepo-specific path mappings (lines 11-43) that should be removed from the template. These paths point to the monorepo's packages directory structure (e.g., "../../packages/firecms_core/src") which won't exist when users create a new project. The template should only use npm package imports like "@firecms/core" without custom path mappings, as these packages will be installed in node_modules.
| First, install dependencies from the monorepo root: | ||
|
|
||
| ```bash | ||
| npm install | ||
| ``` | ||
|
|
||
| Then, run the development server: | ||
|
|
||
| ```bash | ||
| cd examples/example_astro | ||
| npm run dev | ||
| ``` | ||
|
|
||
| Open [http://localhost:4321/cms](http://localhost:4321/cms) with your browser to access the FireCMS dashboard. | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ``` | ||
| src/ | ||
| ├── cms/ # FireCMS React components | ||
| │ ├── App.tsx # Main FireCMS app component | ||
| │ ├── CMSRoute.tsx # Client-side routing wrapper | ||
| │ ├── collections/ # Firestore collection definitions | ||
| │ │ ├── products.tsx | ||
| │ │ ├── blog.tsx | ||
| │ │ ├── users_collection.tsx | ||
| │ │ └── locales.tsx | ||
| │ ├── components/ | ||
| │ │ └── CustomLoginView.tsx | ||
| │ └── views/ | ||
| │ ├── ExampleCMSView.tsx | ||
| │ └── TestEditorView.tsx | ||
| ├── common/ # Shared config and types | ||
| │ ├── firebase_config.ts | ||
| │ └── types.ts | ||
| ├── pages/ # Astro pages (file-based routing) | ||
| │ ├── index.astro # Redirects to /cms | ||
| │ └── cms/ | ||
| │ └── [...path].astro # Catch-all route for FireCMS | ||
| └── styles/ | ||
| └── index.css # Tailwind CSS + FireCMS styles | ||
| ``` | ||
|
|
||
| ## How It Works | ||
|
|
||
| Astro renders pages statically by default, but FireCMS is a fully interactive React application that requires client-side rendering. This example uses Astro's `client:only="react"` directive to render the CMS entirely on the client side, avoiding SSR for components that depend on browser APIs like `window` and `BrowserRouter`. | ||
|
|
||
| The catch-all route `[...path].astro` ensures all CMS sub-routes (`/cms/products`, `/cms/blog`, etc.) are handled by the React Router inside FireCMS. | ||
|
|
||
| ## Learn More | ||
|
|
||
| - [FireCMS Documentation](https://firecms.co/docs) | ||
| - [Astro Documentation](https://docs.astro.build) | ||
| - [Astro React Integration](https://docs.astro.build/en/guides/integrations-guide/react/) | ||
|
|
||
| ## Deploy | ||
|
|
||
| You can deploy this Astro app to any static hosting provider or use SSR adapters for platforms like Netlify, Vercel, or Cloudflare. |
There was a problem hiding this comment.
The README references a monorepo structure that doesn't apply to projects created from the template. Lines 43-44 state "Redirects to /cms" and "Catch-all route for FireCMS", but the index.astro page actually provides a welcome page with navigation, not a redirect. Additionally, the installation and running instructions (lines 7-18) assume the user is in a monorepo context ("install dependencies from the monorepo root", "cd examples/example_astro"), which won't be true for projects created via the CLI. These instructions should be updated for standalone project usage.
| import "@fontsource/poppins"; | ||
| import "@fontsource/playfair-display"; | ||
| import "@fontsource/jetbrains-mono"; |
There was a problem hiding this comment.
These font imports (Poppins, Playfair Display, JetBrains Mono) are not used in this blog post view. The page uses inline styles that default to system fonts. Only the Rubik font family is referenced in the main layout. These unused imports should be removed to improve bundle size.
Add a new "astro" template option to the
firecms initcommand, allowing users to scaffold a FireCMS project with Astro SSG/SSR and blog support.template_astroto CLI templates with templatized Firebase config, cleaned monorepo aliases, and npm package dependencies--astroflag, interactive prompt option, template folder mapping, post-creation instructions, and file replacement logic