Rust library to generate static websites
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: content -> full rebuild

+97 -3
+4 -3
crates/maudit/src/build.rs
··· 312 312 print_title("initializing content sources"); 313 313 314 314 // Determine which content sources need to be initialized 315 - // For incremental builds, only re-init sources whose files have changed 316 - let sources_to_init: Option<FxHashSet<String>> = if is_incremental { 315 + // For incremental builds with specific routes to rebuild, only re-init sources whose files have changed 316 + // If routes_to_rebuild is None (full rebuild), always init all sources 317 + let sources_to_init: Option<FxHashSet<String>> = if routes_to_rebuild.is_some() { 317 318 if let Some(changed) = changed_files { 318 319 build_state.get_affected_content_sources(changed) 319 320 } else { 320 321 None // Full init 321 322 } 322 323 } else { 323 - None // Full init 324 + None // Full init (routes_to_rebuild is None means full rebuild) 324 325 }; 325 326 326 327 // Initialize content sources (all or selective)
+93
e2e/tests/incremental-build.spec.ts
··· 928 928 expect(after.articleThird).not.toBe(before.articleThird); 929 929 expect(after.articles).not.toBe(before.articles); 930 930 }); 931 + 932 + // ============================================================ 933 + // TEST 15: Full rebuild from untracked file properly initializes content sources 934 + // ============================================================ 935 + test("full rebuild from untracked file properly initializes content sources", async ({ devServer }) => { 936 + // This test verifies that when an untracked Rust file (like helpers.rs) changes, 937 + // triggering a full rebuild (routes_to_rebuild = None), content sources are 938 + // still properly initialized. 939 + // 940 + // This was a bug where the code checked `is_incremental` instead of 941 + // `routes_to_rebuild.is_some()`, causing content sources to not be initialized 942 + // during full rebuilds triggered by untracked file changes. 943 + // 944 + // Setup: 945 + // - helpers.rs is a shared module not tracked in source_to_routes 946 + // - Changing it triggers routes_to_rebuild = None (full rebuild) 947 + // - Routes like /articles/* use content from the "articles" content source 948 + // - If content sources aren't initialized, the build would crash 949 + // 950 + // This test: 951 + // 1. First modifies a content file to ensure specific content exists 952 + // 2. Then modifies helpers.rs to trigger a full rebuild 953 + // 3. Verifies the content-using routes are properly built with correct content 954 + 955 + const helpersRs = resolve(fixturePath, "src", "pages", "helpers.rs"); 956 + const originalHelpersRs = readFileSync(helpersRs, "utf-8"); 957 + const rsTimeout = 60000; 958 + 959 + try { 960 + // Step 1: Modify content file to set up specific content we can verify 961 + const testMarker = `CONTENT-INIT-TEST-${Date.now()}`; 962 + const newContent = (originals.firstPost as string).replace( 963 + "first post", 964 + `first post - ${testMarker}` 965 + ); 966 + writeFileSync(contentFiles.firstPost, newContent); 967 + 968 + // Wait for the content change to be processed 969 + const beforeContent = getBuildId(htmlPaths.articleFirst); 970 + await waitForBuildComplete(devServer, rsTimeout); 971 + await waitForBuildIdChange(htmlPaths.articleFirst, beforeContent, rsTimeout); 972 + 973 + // Verify the content was updated 974 + let articleHtml = readFileSync(htmlPaths.articleFirst, "utf-8"); 975 + expect(articleHtml).toContain(testMarker); 976 + 977 + // Record build IDs before the helpers.rs change 978 + const before = recordBuildIds(htmlPaths); 979 + expect(before.articleFirst).not.toBeNull(); 980 + expect(before.articles).not.toBeNull(); 981 + 982 + // Step 2: Modify helpers.rs to trigger full rebuild 983 + // This is an untracked file, so it triggers routes_to_rebuild = None 984 + devServer.clearLogs(); 985 + writeFileSync(helpersRs, originalHelpersRs + `\n// content-init-test-${Date.now()}`); 986 + 987 + await waitForBuildComplete(devServer, rsTimeout); 988 + await waitForBuildIdChange(htmlPaths.articleFirst, before.articleFirst, rsTimeout); 989 + 990 + // Step 3: Verify the build succeeded and content is still correct 991 + // If content sources weren't initialized, this would fail or crash 992 + const after = recordBuildIds(htmlPaths); 993 + 994 + // All routes should be rebuilt (full rebuild) 995 + expect(after.index).not.toBe(before.index); 996 + expect(after.about).not.toBe(before.about); 997 + expect(after.blog).not.toBe(before.blog); 998 + expect(after.articleFirst).not.toBe(before.articleFirst); 999 + expect(after.articles).not.toBe(before.articles); 1000 + 1001 + // Most importantly: verify the content-using routes have correct content 1002 + // This proves content sources were properly initialized during the full rebuild 1003 + articleHtml = readFileSync(htmlPaths.articleFirst, "utf-8"); 1004 + expect(articleHtml).toContain(testMarker); 1005 + 1006 + // Also verify the articles list page works (uses entries()) 1007 + const articlesHtml = readFileSync(htmlPaths.articles, "utf-8"); 1008 + expect(articlesHtml).toContain("First Post"); 1009 + 1010 + } finally { 1011 + // Restore original content 1012 + writeFileSync(helpersRs, originalHelpersRs); 1013 + writeFileSync(contentFiles.firstPost, originals.firstPost as string); 1014 + 1015 + // Wait for restoration build 1016 + const beforeRestore = getBuildId(htmlPaths.articleFirst); 1017 + try { 1018 + await waitForBuildIdChange(htmlPaths.articleFirst, beforeRestore, 60000); 1019 + } catch { 1020 + // Restoration build may not always complete, that's ok 1021 + } 1022 + } 1023 + }); 931 1024 });