diff --git a/.gitignore b/.gitignore
index a46dc15..c367983 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/dist/
bbrew
+node_modules
diff --git a/Makefile b/Makefile
index de29faf..8e2eba4 100644
--- a/Makefile
+++ b/Makefile
@@ -35,4 +35,18 @@ run: build
##############################
.PHONY: lint
lint:
- @golangci-lint run
\ No newline at end of file
+ @golangci-lint run
+
+##############################
+# WEBSITE
+##############################
+.PHONY: build-site
+build-site:
+ @node build.js
+
+.PHONY: serve-site
+serve-site:
+ @npx http-server docs -p 3000
+
+.PHONY: dev-site
+dev-site: build-site serve-site
\ No newline at end of file
diff --git a/build.js b/build.js
new file mode 100644
index 0000000..f1a2f2a
--- /dev/null
+++ b/build.js
@@ -0,0 +1,228 @@
+const ejs = require('ejs');
+const fs = require('fs');
+const path = require('path');
+const marked = require('marked');
+const frontMatter = require('front-matter');
+const ejsLayouts = require('ejs-layouts');
+
+// Configuration
+const config = {
+ srcDir: 'site',
+ distDir: 'docs',
+ templatesDir: 'site/templates',
+ contentDir: 'site/content',
+ site: {
+ name: 'Bold Brew',
+ description: 'A modern TUI for Homebrew',
+ url: 'https://bold-brew.com'
+ }
+};
+
+// Function to generate a page
+async function generatePage(template, data, outputPath) {
+ const templatePath = path.join(config.templatesDir, template);
+ const templateContent = fs.readFileSync(templatePath, 'utf-8');
+ const layoutPath = path.join(config.templatesDir, 'layout.ejs');
+ const layoutContent = fs.readFileSync(layoutPath, 'utf-8');
+
+ // Render the template content
+ const content = ejs.render(templateContent, {
+ ...data,
+ filename: templatePath
+ });
+
+ // Render the layout with the content
+ const html = ejs.render(layoutContent, {
+ ...data,
+ filename: layoutPath,
+ content
+ });
+
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
+ fs.writeFileSync(outputPath, html);
+}
+
+// Function to generate the homepage
+async function generateHomepage() {
+ const posts = getBlogPosts();
+ await generatePage('index.ejs', {
+ title: 'Bold Brew (bbrew) - Modern Homebrew TUI Manager for macOS',
+ description: 'Bold Brew (bbrew) is the modern Terminal User Interface for Homebrew on macOS. Install, update, and manage packages with an elegant TUI. The perfect alternative to traditional Homebrew commands.',
+ keywords: 'bbrew, Bold Brew, Homebrew TUI, macOS package manager, Homebrew GUI, terminal package manager, Homebrew alternative, macOS development tools',
+ canonicalUrl: config.site.url,
+ ogType: 'website',
+ posts,
+ site: config.site
+ }, path.join(config.distDir, 'index.html'));
+}
+
+// Function to generate the blog
+async function generateBlog() {
+ // Generate the main blog page
+ await generatePage('blog/index.ejs', {
+ title: 'Blog | Bold Brew (bbrew)',
+ description: 'Tips, tutorials, and guides for managing Homebrew packages on macOS',
+ keywords: 'Homebrew blog, macOS tutorials, package management, Bold Brew guides',
+ canonicalUrl: `${config.site.url}/blog/`,
+ ogType: 'website',
+ breadcrumb: [
+ { text: 'Home', url: '/' },
+ { text: 'Blog', url: '/blog/' }
+ ],
+ posts: getBlogPosts(),
+ site: config.site
+ }, path.join(config.distDir, 'blog/index.html'));
+
+ // Generate article pages
+ const blogDir = path.join(__dirname, config.contentDir, 'blog');
+ if (fs.existsSync(blogDir)) {
+ const files = fs.readdirSync(blogDir)
+ .filter(file => file.endsWith('.md'));
+
+ for (const file of files) {
+ const filePath = path.join(blogDir, file);
+ const content = fs.readFileSync(filePath, 'utf8');
+ const { attributes, body } = frontMatter(content);
+ const htmlContent = marked.parse(body);
+ const outputFile = file.replace('.md', '.html');
+
+ await generatePage('blog/post.ejs', {
+ title: attributes.title || '',
+ description: attributes.description || '',
+ keywords: attributes.keywords || 'Homebrew, macOS, package management, Bold Brew, bbrew, terminal, development tools',
+ date: attributes.date || '',
+ content: htmlContent,
+ canonicalUrl: `${config.site.url}/blog/${outputFile}`,
+ ogType: 'article',
+ breadcrumb: [
+ { text: 'Home', url: '/' },
+ { text: 'Blog', url: '/blog/' },
+ { text: attributes.title || '', url: `/blog/${outputFile}` }
+ ],
+ site: config.site
+ }, path.join(config.distDir, 'blog', outputFile));
+ }
+ }
+}
+
+function getBlogPosts() {
+ const blogDir = path.join(__dirname, config.contentDir, 'blog');
+ const posts = [];
+
+ if (!fs.existsSync(blogDir)) {
+ return posts;
+ }
+
+ const files = fs.readdirSync(blogDir)
+ .filter(file => file.endsWith('.md'));
+
+ for (const file of files) {
+ const content = fs.readFileSync(path.join(blogDir, file), 'utf8');
+ const { attributes } = frontMatter(content);
+ const outputFile = file.replace('.md', '.html');
+
+ if (attributes.title && attributes.date) {
+ posts.push({
+ title: attributes.title,
+ date: attributes.date,
+ url: `/blog/${outputFile}`,
+ excerpt: attributes.description || ''
+ });
+ }
+ }
+
+ return posts.sort((a, b) => new Date(b.date) - new Date(a.date));
+}
+
+// Function to generate the sitemap
+async function generateSitemap() {
+ const posts = getBlogPosts();
+ const baseUrl = config.site.url;
+ const today = new Date().toISOString().split('T')[0];
+
+ // Static pages
+ const staticPages = [
+ {
+ url: '/',
+ lastmod: today,
+ changefreq: 'weekly',
+ priority: '1.0'
+ },
+ {
+ url: '/blog/',
+ lastmod: today,
+ changefreq: 'weekly',
+ priority: '0.9'
+ }
+ ];
+
+ // Blog pages
+ const blogPages = posts.map(post => ({
+ url: post.url,
+ lastmod: post.date,
+ changefreq: 'monthly',
+ priority: '0.8'
+ }));
+
+ // Combine all pages
+ const allPages = [...staticPages, ...blogPages];
+
+ // Generate XML content
+ const sitemapContent = `
+