@@ -177,17 +177,43 @@ function copyTree (srcDir, destDir, sourceRoot, inputRelDir) {
177177 }
178178}
179179
180+ // Removes only the entries under destDir that no longer exist in the source - never
181+ // submit.njk (this repo's own "Submit Your Own" page, not something blueprint-library
182+ // provides) and never an entry copyTree is about to repopulate anyway. Deliberately
183+ // narrower than docs-sync.mjs's full wipe: copyTree already overwrites every file in
184+ // place on each sync (11ty sees a cheap "changed" event), so wiping unaffected blueprints
185+ // too would turn that into a delete+recreate of the entire tree on every sync - noisy for
186+ // 11ty's watcher and briefly 404s a page mid-rebuild for no reason.
187+ function clearOrphans ( destDir , currentNames ) {
188+ if ( ! existsSync ( destDir ) ) return
189+ for ( const entry of readdirSync ( destDir , { withFileTypes : true } ) ) {
190+ if ( entry . name === 'submit.njk' || currentNames . has ( entry . name ) ) continue
191+ rmSync ( join ( destDir , entry . name ) , { recursive : true , force : true } )
192+ }
193+ }
194+
195+ function directoryNames ( dir ) {
196+ return readdirSync ( dir , { withFileTypes : true } )
197+ . filter ( entry => entry . isDirectory ( ) && ! entry . name . startsWith ( '.' ) )
198+ }
199+
180200/**
181- * Populate src/blueprints from `dir` (one folder per blueprint) and return the manifest
182- * describing what was published.
201+ * Populate src/blueprints from `dir` (one category folder per top-level entry, one
202+ * blueprint per folder below that) and return the manifest describing what was published.
203+ * Pruned one level at a time - at the category level and again inside each still-current
204+ * category - since that's the actual unit that gets renamed or removed upstream, without
205+ * having to fully tree-diff every nested asset to catch it.
183206 */
184207function writeBlueprints ( { dir, websiteRoot, kind, ref } ) {
185208 const destRoot = join ( websiteRoot , 'src' , 'blueprints' )
186-
187- for ( const entry of readdirSync ( dir , { withFileTypes : true } ) ) {
188- if ( ! entry . isDirectory ( ) || entry . name . startsWith ( '.' ) ) continue
189- const srcDir = join ( dir , entry . name )
190- copyTree ( srcDir , join ( destRoot , basename ( srcDir ) ) , dir , join ( 'blueprints' , basename ( srcDir ) ) )
209+ const categories = directoryNames ( dir )
210+ clearOrphans ( destRoot , new Set ( categories . map ( entry => entry . name ) ) )
211+
212+ for ( const category of categories ) {
213+ const categorySrcDir = join ( dir , category . name )
214+ const categoryDestDir = join ( destRoot , basename ( categorySrcDir ) )
215+ clearOrphans ( categoryDestDir , new Set ( directoryNames ( categorySrcDir ) . map ( entry => entry . name ) ) )
216+ copyTree ( categorySrcDir , categoryDestDir , dir , join ( 'blueprints' , basename ( categorySrcDir ) ) )
191217 }
192218
193219 return {
0 commit comments