Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@
"devDependencies": {
"@sparticuz/chromium": "^143.0.4",
"prettier": "^3.6.2",
"puppeteer-core": "^24.40.0"
"puppeteer-core": "^24.40.0",
"sitemap": "^9.0.1"
},
"scripts": {
"start": "react-scripts start",
"prebuild": "node scripts/generate-sitemap.js",
"build": "react-scripts build",
Comment on lines 40 to 49
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added sitemap@^9.0.1 devDependency declares an engines requirement of node >=20.19.5 (see package-lock), but this repo’s CI matrix includes Node 16.x and 18.x. This is likely to break npm ci and/or npm run build in those environments because prebuild runs the sitemap generator. Consider pinning sitemap to a version that supports the project’s supported Node versions, or updating the supported Node/CI matrix accordingly (and documenting it).

Copilot uses AI. Check for mistakes.
"postbuild": "node scripts/prerender.js",
"test": "react-scripts test",
Expand Down
65 changes: 1 addition & 64 deletions public/sitemap.xml
Original file line number Diff line number Diff line change
@@ -1,64 +1 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
xmlns:xhtml="http://www.w3.org/1999/xhtml">

<!-- Homepage -->
<url>
<loc>https://refactron.dev/</loc>
<lastmod>2026-03-26</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
<image:image>
<image:loc>https://refactron.dev/Refactron-logo-TM.png</image:loc>
<image:title>Refactron - Safe, Verified Refactoring</image:title>
<image:caption>Safety-first refactoring tool that analyzes code structure and proposes incremental, reviewable changes with verification</image:caption>
</image:image>
</url>

<!-- About Page -->
<url>
<loc>https://refactron.dev/about</loc>
<lastmod>2026-03-26</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>

<!-- Case Studies -->
<url>
<loc>https://refactron.dev/case-studies</loc>
<lastmod>2026-03-26</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
<image:image>
<image:loc>https://refactron.dev/Refactron-logo-TM.png</image:loc>
<image:title>Refactron Case Studies - Real Code Transformations</image:title>
<image:caption>Real transformations from engineering teams using safety-first refactoring</image:caption>
</image:image>
</url>

<!-- Case Study: Legacy Code AI Refactoring (Actual Data) -->
<url>
<loc>https://refactron.dev/case-studies/legacy-code-ai-refactoring</loc>
<lastmod>2026-03-26</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>

<!-- Privacy Policy -->
<url>
<loc>https://refactron.dev/privacy-policy</loc>
<lastmod>2026-03-26</lastmod>
<changefreq>monthly</changefreq>
<priority>0.4</priority>
</url>

<!-- Terms of Service -->
<url>
<loc>https://refactron.dev/terms-of-service</loc>
<lastmod>2026-03-26</lastmod>
<changefreq>monthly</changefreq>
<priority>0.4</priority>
</url>

</urlset>
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://refactron.dev/</loc><changefreq>weekly</changefreq><priority>1.0</priority></url><url><loc>https://refactron.dev/blog</loc><changefreq>weekly</changefreq><priority>0.9</priority></url><url><loc>https://refactron.dev/about</loc><changefreq>monthly</changefreq><priority>0.6</priority></url><url><loc>https://refactron.dev/changelog</loc><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://refactron.dev/security</loc><changefreq>monthly</changefreq><priority>0.5</priority></url><url><loc>https://refactron.dev/privacy-policy</loc><changefreq>yearly</changefreq><priority>0.3</priority></url><url><loc>https://refactron.dev/terms-of-service</loc><changefreq>yearly</changefreq><priority>0.3</priority></url><url><loc>https://refactron.dev/blog/i-ran-refactron-on-djangos-codebase</loc><changefreq>monthly</changefreq><priority>0.8</priority></url><url><loc>https://refactron.dev/blog/refactron-vs-cursor-vs-codeant</loc><changefreq>monthly</changefreq><priority>0.8</priority></url><url><loc>https://refactron.dev/blog/why-we-built-verification-engine-first</loc><changefreq>monthly</changefreq><priority>0.8</priority></url><url><loc>https://refactron.dev/blog/legacy-code-ai-refactoring</loc><changefreq>monthly</changefreq><priority>0.8</priority></url><url><loc>https://refactron.dev/blog/refactron-on-requests-library</loc><changefreq>monthly</changefreq><priority>0.8</priority></url><url><loc>https://refactron.dev/blog/real-cost-of-not-refactoring</loc><changefreq>monthly</changefreq><priority>0.8</priority></url><url><loc>https://refactron.dev/blog/refactron-on-fastapi</loc><changefreq>monthly</changefreq><priority>0.8</priority></url><url><loc>https://refactron.dev/blog/how-to-safely-refactor-python-code-you-didnt-write</loc><changefreq>monthly</changefreq><priority>0.8</priority></url></urlset>
48 changes: 48 additions & 0 deletions scripts/generate-sitemap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const { SitemapStream, streamToPromise } = require('sitemap');
const { Readable } = require('stream');
const fs = require('fs');
const path = require('path');

const BASE_URL = 'https://refactron.dev';

// Extract slugs from posts.ts via regex — no ts-node needed
const postsFile = fs.readFileSync(
path.join(__dirname, '../src/data/posts.ts'),
'utf-8'
);
const slugMatches = [...postsFile.matchAll(/slug:\s*['"]([^'"]+)['"]/g)];
const blogSlugs = slugMatches.map(m => m[1]);

const staticRoutes = [
{ url: '/', changefreq: 'weekly', priority: 1.0 },
{ url: '/blog', changefreq: 'weekly', priority: 0.9 },
{ url: '/about', changefreq: 'monthly', priority: 0.6 },
{ url: '/changelog', changefreq: 'weekly', priority: 0.7 },
{ url: '/security', changefreq: 'monthly', priority: 0.5 },
{ url: '/privacy-policy', changefreq: 'yearly', priority: 0.3 },
{ url: '/terms-of-service',changefreq: 'yearly', priority: 0.3 },
Comment on lines +16 to +23
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/pricing is included as a static sitemap route, but the app routes in src/App.tsx do not define a /pricing page (pricing appears to be a section on the landing page). This will publish a broken URL in sitemap.xml and hurt SEO/crawl quality. Either remove /pricing from the sitemap or add a real /pricing route/page.

Copilot uses AI. Check for mistakes.
// blog posts added dynamically below
];

const blogRoutes = blogSlugs.map(slug => ({
url: `/blog/${slug}`,
changefreq: 'monthly',
priority: 0.8,
}));

async function generate() {
const links = [...staticRoutes, ...blogRoutes];
const stream = new SitemapStream({ hostname: BASE_URL });
const xml = await streamToPromise(Readable.from(links).pipe(stream));

const outputPath = path.join(__dirname, '../public/sitemap.xml');
fs.writeFileSync(outputPath, xml.toString());
console.log(
`✓ Sitemap generated — ${blogSlugs.length} blog posts + ${staticRoutes.length} static routes → public/sitemap.xml`
);
}

generate().catch(err => {
console.error('Sitemap generation failed:', err);
process.exit(1);
});
16 changes: 14 additions & 2 deletions scripts/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,25 @@ const path = require('path');
const fs = require('fs');
const http = require('http');

// Pages to pre-render (public marketing pages only)
// Extract blog slugs from posts.ts via regex (same approach as generate-sitemap.js)
const postsFile = fs.readFileSync(
path.join(__dirname, '../src/data/posts.ts'),
'utf-8'
);
const blogSlugs = [...postsFile.matchAll(/slug:\s*['"]([^'"]+)['"]/g)].map(
m => m[1]
);

// Pages to pre-render
const PAGES = [
'/',
'/case-studies',
'/blog',
'/about',
'/changelog',
'/security',
'/privacy-policy',
'/terms-of-service',
...blogSlugs.map(slug => `/blog/${slug}`),
];

const BUILD_DIR = path.resolve(__dirname, '..', 'build');
Expand Down
40 changes: 33 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import {
BrowserRouter as Router,
Routes,
Route,
Navigate,
} from 'react-router-dom';
import { Analytics } from '@vercel/analytics/react';
import HeroSection from './components/HeroSection';
import RefactoringWorkflowSection from './components/RefactoringWorkflowSection';
import WhatWeDoSection from './components/WhatWeDoSection';
import PricingSection from './components/PricingSection';
import FAQSection from './components/FAQSection';
import CaseStudiesPage from './components/CaseStudiesPage';
import CaseStudyDetailPage from './components/CaseStudyDetailPage';
import BlogPage from './components/BlogPage';
import BlogPostPage from './components/BlogPostPage';
import AboutPage from './components/AboutPage';
import PrivacyPolicy from './components/PrivacyPolicy';
import TermsOfService from './components/TermsOfService';
Expand Down Expand Up @@ -98,19 +103,28 @@ function App() {
</PageLayout>
}
/>
{/* Legacy redirects — preserve old /case-studies URLs */}
<Route
path="/case-studies"
element={<Navigate to="/blog" replace />}
/>
<Route
path="/case-studies/:slug"
element={<Navigate to="/blog" replace />}
/>
<Route
path="/blog"
element={
<PageLayout>
<CaseStudiesPage />
<BlogPage />
</PageLayout>
}
/>
<Route
path="/case-studies/:slug"
path="/blog/:slug"
element={
<PageLayout mainClassName="pt-0 sm:pt-0">
<CaseStudyDetailPage />
<BlogPostPage />
</PageLayout>
}
/>
Expand Down Expand Up @@ -162,7 +176,19 @@ function App() {
<CookieManager />
</>
)}
<Analytics />
<Analytics
beforeSend={event => {
try {
const consent = localStorage.getItem('cookie-consent');
const prefs = localStorage.getItem('cookie-preferences');
if (!consent || !prefs) return null;
const parsed = JSON.parse(prefs);
return parsed.analytics === true ? event : null;
} catch {
return null;
}
}}
/>
</Router>
</ErrorBoundary>
</ThemeProvider>
Expand Down
Loading
Loading