Skip to content

Commit 65fa415

Browse files
ericyangpanclaude
andcommitted
chore(scripts): improve build scripts
Enhance build scripts for better maintainability: - Update generate-metadata.mjs to read locales from i18n config - Auto-generate articles.ts and docs.ts with component imports - Improve sort-manifest-fields.mjs for consistent field ordering - Add better error handling and logging These improvements make the build process more robust and reduce manual configuration when adding new content or languages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 478d285 commit 65fa415

File tree

2 files changed

+329
-91
lines changed

2 files changed

+329
-91
lines changed

scripts/generate-metadata.mjs

Lines changed: 133 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ const __filename = fileURLToPath(import.meta.url);
1414
const __dirname = path.dirname(__filename);
1515
const rootDir = path.join(__dirname, '..');
1616

17+
// Read supported locales from i18n config
18+
function getSupportedLocales() {
19+
const configPath = path.join(rootDir, 'src/i18n/config.ts');
20+
const configContent = fs.readFileSync(configPath, 'utf8');
21+
// Extract locales array from: export const locales = ['en', 'zh-Hans', 'de'] as const;
22+
const match = configContent.match(/export const locales = \[([^\]]+)\]/);
23+
if (!match) {
24+
throw new Error('Could not find locales export in src/i18n/config.ts');
25+
}
26+
// Parse the array: "'en', 'zh-Hans', 'de'" -> ['en', 'zh-Hans', 'de']
27+
return match[1].split(',').map(s => s.trim().replace(/['"]/g, ''));
28+
}
29+
30+
const SUPPORTED_LOCALES = getSupportedLocales();
31+
1732
function getMDXFiles(directory) {
1833
return fs.readdirSync(directory).filter(file => file.endsWith('.mdx'));
1934
}
@@ -28,11 +43,6 @@ function getSlugFromFilename(fileName) {
2843
return fileName.replace(/\.mdx$/, '');
2944
}
3045

31-
function extractH1FromMDX(content) {
32-
const match = content.match(/^#\s+(.+)$/m);
33-
return match ? match[1].trim() : '';
34-
}
35-
3646
// Parse FAQ sections from a unified index.mdx file
3747
// Each H1 heading becomes a FAQ item with its title and content
3848
function parseFaqSections(content) {
@@ -43,7 +53,6 @@ function parseFaqSections(content) {
4353
const sections = [];
4454
const h1Regex = /^# (.+)$/gm;
4555
let match;
46-
let lastIndex = 0;
4756
const matches = [];
4857

4958
// Find all H1 headings and their positions
@@ -106,7 +115,6 @@ function generateArticlesMetadataForLocale(locale) {
106115

107116
// Generate articles metadata for all locales
108117
function generateArticlesMetadata() {
109-
const SUPPORTED_LOCALES = ['en', 'zh-Hans'];
110118
const articlesMetadata = {};
111119

112120
for (const locale of SUPPORTED_LOCALES) {
@@ -145,7 +153,6 @@ function generateDocsMetadataForLocale(locale) {
145153

146154
// Generate docs metadata for all locales
147155
function generateDocsMetadata() {
148-
const SUPPORTED_LOCALES = ['en', 'zh-Hans'];
149156
const docsMetadata = {};
150157

151158
for (const locale of SUPPORTED_LOCALES) {
@@ -186,7 +193,6 @@ function generateFaqMetadataForLocale(locale) {
186193

187194
// Generate FAQ metadata for all locales
188195
function generateFaqMetadata() {
189-
const SUPPORTED_LOCALES = ['en', 'zh-Hans'];
190196
const faqMetadata = {};
191197

192198
for (const locale of SUPPORTED_LOCALES) {
@@ -208,9 +214,6 @@ function generateStackCounts() {
208214
vendors: 'vendors',
209215
};
210216

211-
// Single files (old structure)
212-
const singleFiles = {};
213-
214217
const stackCounts = {};
215218

216219
// Count files in directories
@@ -232,27 +235,45 @@ function generateStackCounts() {
232235
}
233236
}
234237

235-
// Count items in single files
236-
for (const [stackId, fileName] of Object.entries(singleFiles)) {
237-
const manifestPath = path.join(rootDir, 'manifests', fileName);
238+
return stackCounts;
239+
}
238240

239-
if (!fs.existsSync(manifestPath)) {
240-
console.warn(`⚠️ Manifest file not found: ${manifestPath}`);
241-
stackCounts[stackId] = 0;
242-
continue;
241+
// Generate component imports for articles
242+
function generateArticleComponentsCode(articles) {
243+
const locales = Object.keys(articles);
244+
const componentLines = [];
245+
246+
for (const locale of locales) {
247+
const localeArticles = articles[locale];
248+
const componentEntries = localeArticles.map(article =>
249+
` '${article.slug}': require('@content/articles/${locale}/${article.slug}.mdx').default,`
250+
).join('\n');
251+
252+
if (componentEntries) {
253+
componentLines.push(` '${locale}': {\n${componentEntries}\n },`);
243254
}
255+
}
244256

245-
try {
246-
const fileContents = fs.readFileSync(manifestPath, 'utf8');
247-
const manifestData = JSON.parse(fileContents);
248-
stackCounts[stackId] = Array.isArray(manifestData) ? manifestData.length : 0;
249-
} catch (error) {
250-
console.error(`❌ Error reading ${fileName}:`, error.message);
251-
stackCounts[stackId] = 0;
257+
return `const articleComponents: Record<string, Record<string, React.ComponentType>> = {\n${componentLines.join('\n')}\n};`;
258+
}
259+
260+
// Generate component imports for docs
261+
function generateDocComponentsCode(docs) {
262+
const locales = Object.keys(docs);
263+
const componentLines = [];
264+
265+
for (const locale of locales) {
266+
const localeDocs = docs[locale];
267+
const componentEntries = localeDocs.map(doc =>
268+
` '${doc.slug}': require('@content/docs/${locale}/${doc.slug}.mdx').default,`
269+
).join('\n');
270+
271+
if (componentEntries) {
272+
componentLines.push(` '${locale}': {\n${componentEntries}\n },`);
252273
}
253274
}
254275

255-
return stackCounts;
276+
return `const docComponents: Record<string, Record<string, React.ComponentType>> = {\n${componentLines.join('\n')}\n};`;
256277
}
257278

258279
// Main execution
@@ -277,8 +298,8 @@ function main() {
277298
const content = `// This file is auto-generated by scripts/generate-metadata.mjs
278299
// DO NOT EDIT MANUALLY
279300
280-
import type { ArticleMetadata } from '../articles';
281-
import type { DocSection } from '../docs';
301+
import type { ArticleMetadata } from './articles';
302+
import type { DocSection } from './docs';
282303
import type { CollectionSection } from '../collections';
283304
import type { FaqItem } from '../faq';
284305
@@ -295,6 +316,85 @@ export const stackCounts: Record<string, number> = ${JSON.stringify(stackCounts,
295316

296317
fs.writeFileSync(outputFile, content, 'utf8');
297318

319+
// Generate articles.ts file in generated directory
320+
const articlesGeneratedFile = path.join(outputDir, 'articles.ts');
321+
const articlesComponentsCode = generateArticleComponentsCode(articles);
322+
const articlesGeneratedContent = `// This file is auto-generated by scripts/generate-metadata.mjs
323+
// DO NOT EDIT MANUALLY
324+
325+
import { articlesMetadata } from './metadata';
326+
327+
export type ArticleMetadata = {
328+
title: string;
329+
description: string;
330+
date: string;
331+
slug: string;
332+
};
333+
334+
// Get articles for a specific locale with fallback to English
335+
export function getArticles(locale: string = 'en'): ArticleMetadata[] {
336+
return articlesMetadata[locale] || articlesMetadata['en'] || [];
337+
}
338+
339+
// Get all articles (backward compatibility)
340+
export const articles: ArticleMetadata[] = getArticles('en');
341+
342+
// Get a specific article by slug for a given locale
343+
export function getArticleBySlug(slug: string, locale: string = 'en'): ArticleMetadata | undefined {
344+
const localeArticles = getArticles(locale);
345+
return localeArticles.find((article) => article.slug === slug);
346+
}
347+
348+
// MDX components mapping for all locales (webpack will handle this at build time)
349+
${articlesComponentsCode}
350+
351+
// Get components for a specific locale with fallback to English
352+
export function getArticleComponents(locale: string = 'en'): Record<string, React.ComponentType> {
353+
return articleComponents[locale] || articleComponents['en'] || {};
354+
}
355+
`;
356+
357+
fs.writeFileSync(articlesGeneratedFile, articlesGeneratedContent, 'utf8');
358+
359+
// Generate docs.ts file in generated directory
360+
const docsGeneratedFile = path.join(outputDir, 'docs.ts');
361+
const docsComponentsCode = generateDocComponentsCode(docs);
362+
const docsGeneratedContent = `// This file is auto-generated by scripts/generate-metadata.mjs
363+
// DO NOT EDIT MANUALLY
364+
365+
import { docsMetadata } from './metadata';
366+
367+
export type DocSection = {
368+
id: string;
369+
title: string;
370+
slug: string;
371+
};
372+
373+
// Get doc sections for a specific locale with fallback to English
374+
export function getDocSections(locale: string): DocSection[] {
375+
return docsMetadata[locale] || docsMetadata['en'] || [];
376+
}
377+
378+
// Get all doc sections (backward compatibility)
379+
export const docSections: DocSection[] = getDocSections('en');
380+
381+
// Get a specific doc by slug for a given locale
382+
export function getDocBySlug(slug: string, locale: string = 'en'): DocSection | undefined {
383+
const sections = getDocSections(locale);
384+
return sections.find((doc) => doc.slug === slug);
385+
}
386+
387+
// MDX components mapping for all locales (webpack will handle this at build time)
388+
${docsComponentsCode}
389+
390+
// Get components for a specific locale with fallback to English
391+
export function getDocComponents(locale: string = 'en'): Record<string, React.ComponentType> {
392+
return docComponents[locale] || docComponents['en'] || {};
393+
}
394+
`;
395+
396+
fs.writeFileSync(docsGeneratedFile, docsGeneratedContent, 'utf8');
397+
298398
// Calculate total docs, articles, and faqs across all locales
299399
const totalDocs = Object.values(docs).reduce((sum, localeDocs) => sum + localeDocs.length, 0);
300400
const totalArticles = Object.values(articles).reduce((sum, localeArticles) => sum + localeArticles.length, 0);
@@ -307,7 +407,10 @@ export const stackCounts: Record<string, number> = ${JSON.stringify(stackCounts,
307407

308408
console.log(`✅ Generated metadata for ${totalArticles} articles (${articlesLocalesCounts}), ${totalDocs} docs (${docsLocalesCounts}), ${totalFaqs} faqs (${faqsLocalesCounts}), and collections (${collectionsLocales})`);
309409
console.log(`✅ Generated stack counts: ${stackCountsSummary}`);
310-
console.log(`📝 Output: ${outputFile}`);
410+
console.log(`📝 Generated files:`);
411+
console.log(` - ${outputFile}`);
412+
console.log(` - ${articlesGeneratedFile}`);
413+
console.log(` - ${docsGeneratedFile}`);
311414
}
312415

313416
main();

0 commit comments

Comments
 (0)