diff --git a/src/core/output/outputGenerate.ts b/src/core/output/outputGenerate.ts index 97f93821c..c01175608 100644 --- a/src/core/output/outputGenerate.ts +++ b/src/core/output/outputGenerate.ts @@ -4,7 +4,7 @@ import { XMLBuilder } from 'fast-xml-parser'; import Handlebars from 'handlebars'; import type { RepomixConfigMerged } from '../../config/configSchema.js'; import { RepomixError } from '../../shared/errorHandle.js'; -import { type FileSearchResult, listDirectories, listFiles, searchFiles } from '../file/fileSearch.js'; +import { listDirectories, listFiles, searchFiles } from '../file/fileSearch.js'; import { type FilesByRoot, generateTreeString, generateTreeStringWithRoots } from '../file/fileTreeGenerate.js'; import type { ProcessedFile } from '../file/fileTypes.js'; import type { GitDiffResult } from '../git/gitDiffHandle.js'; @@ -258,6 +258,7 @@ export const generateOutput = async ( gitDiffResult: GitDiffResult | undefined = undefined, gitLogResult: GitLogResult | undefined = undefined, filePathsByRoot?: FilesByRoot[], + emptyDirPaths?: string[], deps = { buildOutputGeneratorContext, generateHandlebarOutput, @@ -277,6 +278,7 @@ export const generateOutput = async ( gitDiffResult, gitLogResult, filePathsByRoot, + emptyDirPaths, ); const renderContext = createRenderContext(outputGeneratorContext); @@ -303,6 +305,7 @@ export const buildOutputGeneratorContext = async ( gitDiffResult: GitDiffResult | undefined = undefined, gitLogResult: GitLogResult | undefined = undefined, filePathsByRoot?: FilesByRoot[], + emptyDirPaths?: string[], deps = { listDirectories, listFiles, @@ -356,21 +359,21 @@ export const buildOutputGeneratorContext = async ( } } else if (config.output.directoryStructure && config.output.includeEmptyDirectories) { // Default behavior: include empty directories only - try { - const merged = (await Promise.all(rootDirs.map((rootDir) => deps.searchFiles(rootDir, config)))).reduce( - (acc: FileSearchResult, curr: FileSearchResult) => - ({ - filePaths: [...acc.filePaths, ...curr.filePaths], - emptyDirPaths: [...acc.emptyDirPaths, ...curr.emptyDirPaths], - }) as FileSearchResult, - { filePaths: [], emptyDirPaths: [] }, - ).emptyDirPaths; - directoryPathsForTree = [...new Set(merged)].sort(); - } catch (error) { - throw new RepomixError( - `Failed to search for empty directories: ${error instanceof Error ? error.message : String(error)}`, - error instanceof Error ? { cause: error } : undefined, - ); + if (emptyDirPaths) { + // Use pre-computed empty directory paths from the initial searchFiles call + directoryPathsForTree = emptyDirPaths; + } else { + // Fallback: if emptyDirPaths not provided (e.g., direct callers like packSkill) + try { + const searchResults = await Promise.all(rootDirs.map((rootDir) => deps.searchFiles(rootDir, config))); + const allEmptyDirs = searchResults.flatMap((result) => result.emptyDirPaths); + directoryPathsForTree = [...new Set(allEmptyDirs)].sort(); + } catch (error) { + throw new RepomixError( + `Failed to search for empty directories: ${error instanceof Error ? error.message : String(error)}`, + error instanceof Error ? { cause: error } : undefined, + ); + } } } diff --git a/src/core/output/outputSplit.ts b/src/core/output/outputSplit.ts index 3cfe14ec5..be8261f6f 100644 --- a/src/core/output/outputSplit.ts +++ b/src/core/output/outputSplit.ts @@ -101,6 +101,7 @@ const renderGroups = async ( gitDiffResult: GitDiffResult | undefined, gitLogResult: GitLogResult | undefined, filePathsByRoot: FilesByRoot[] | undefined, + emptyDirPaths: string[] | undefined, generateOutput: GenerateOutputFn, ): Promise => { const chunkProcessedFiles = groupsToRender.flatMap((g) => g.processedFiles); @@ -115,6 +116,7 @@ const renderGroups = async ( partIndex === 1 ? gitDiffResult : undefined, partIndex === 1 ? gitLogResult : undefined, filePathsByRoot, + emptyDirPaths, ); }; @@ -128,6 +130,7 @@ export const generateSplitOutputParts = async ({ gitLogResult, progressCallback, filePathsByRoot, + emptyDirPaths, deps, }: { rootDirs: string[]; @@ -139,6 +142,7 @@ export const generateSplitOutputParts = async ({ gitLogResult: GitLogResult | undefined; progressCallback: RepomixProgressCallback; filePathsByRoot?: FilesByRoot[]; + emptyDirPaths?: string[]; deps: { generateOutput: GenerateOutputFn; }; @@ -178,6 +182,7 @@ export const generateSplitOutputParts = async ({ gitDiffResult, gitLogResult, filePathsByRoot, + emptyDirPaths, deps.generateOutput, ); const nextBytes = getUtf8ByteLength(nextContent); @@ -215,6 +220,7 @@ export const generateSplitOutputParts = async ({ gitDiffResult, gitLogResult, filePathsByRoot, + emptyDirPaths, deps.generateOutput, ); const singleGroupBytes = getUtf8ByteLength(singleGroupContent); diff --git a/src/core/packager.ts b/src/core/packager.ts index 2bf3c01e2..80b781118 100644 --- a/src/core/packager.ts +++ b/src/core/packager.ts @@ -69,15 +69,18 @@ export const pack = async ( logMemoryUsage('Pack - Start'); progressCallback('Searching for files...'); - const filePathsByDir = await withMemoryLogging('Search Files', async () => + const searchResultsByDir = await withMemoryLogging('Search Files', async () => Promise.all( - rootDirs.map(async (rootDir) => ({ - rootDir, - filePaths: (await deps.searchFiles(rootDir, config, explicitFiles)).filePaths, - })), + rootDirs.map(async (rootDir) => { + const result = await deps.searchFiles(rootDir, config, explicitFiles); + return { rootDir, ...result }; + }), ), ); + const filePathsByDir = searchResultsByDir.map(({ rootDir, filePaths }) => ({ rootDir, filePaths })); + const allEmptyDirPaths = [...new Set(searchResultsByDir.flatMap(({ emptyDirPaths }) => emptyDirPaths))].sort(); + // Sort file paths progressCallback('Sorting files...'); const allFilePaths = filePathsByDir.flatMap(({ filePaths }) => filePaths); @@ -167,6 +170,7 @@ export const pack = async ( gitLogResult, progressCallback, filePathsByRoot, + allEmptyDirPaths, ); const metrics = await withMemoryLogging('Calculate Metrics', () => diff --git a/src/core/packager/produceOutput.ts b/src/core/packager/produceOutput.ts index be67af8cf..02e69a14b 100644 --- a/src/core/packager/produceOutput.ts +++ b/src/core/packager/produceOutput.ts @@ -30,6 +30,7 @@ export const produceOutput = async ( gitLogResult: GitLogResult | undefined, progressCallback: RepomixProgressCallback, filePathsByRoot?: FilesByRoot[], + emptyDirPaths?: string[], overrideDeps: Partial = {}, ): Promise => { const deps = { ...defaultDeps, ...overrideDeps }; @@ -47,6 +48,7 @@ export const produceOutput = async ( gitLogResult, progressCallback, filePathsByRoot, + emptyDirPaths, deps, ); } @@ -60,6 +62,7 @@ export const produceOutput = async ( gitLogResult, progressCallback, filePathsByRoot, + emptyDirPaths, deps, ); }; @@ -74,6 +77,7 @@ const generateAndWriteSplitOutput = async ( gitLogResult: GitLogResult | undefined, progressCallback: RepomixProgressCallback, filePathsByRoot: FilesByRoot[] | undefined, + emptyDirPaths: string[] | undefined, deps: typeof defaultDeps, ): Promise => { const parts = await withMemoryLogging('Generate Split Output', async () => { @@ -87,6 +91,7 @@ const generateAndWriteSplitOutput = async ( gitLogResult, progressCallback, filePathsByRoot, + emptyDirPaths, deps: { generateOutput: deps.generateOutput, }, @@ -124,10 +129,20 @@ const generateAndWriteSingleOutput = async ( gitLogResult: GitLogResult | undefined, progressCallback: RepomixProgressCallback, filePathsByRoot: FilesByRoot[] | undefined, + emptyDirPaths: string[] | undefined, deps: typeof defaultDeps, ): Promise => { const output = await withMemoryLogging('Generate Output', () => - deps.generateOutput(rootDirs, config, processedFiles, allFilePaths, gitDiffResult, gitLogResult, filePathsByRoot), + deps.generateOutput( + rootDirs, + config, + processedFiles, + allFilePaths, + gitDiffResult, + gitLogResult, + filePathsByRoot, + emptyDirPaths, + ), ); progressCallback('Writing output file...'); diff --git a/tests/core/output/diffsInOutput.test.ts b/tests/core/output/diffsInOutput.test.ts index 4b1087a7b..e446605aa 100644 --- a/tests/core/output/diffsInOutput.test.ts +++ b/tests/core/output/diffsInOutput.test.ts @@ -131,6 +131,7 @@ index 123..456 100644 gitDiffResult, undefined, undefined, + undefined, { buildOutputGeneratorContext: mockBuildOutputGeneratorContext, generateHandlebarOutput: mockGenerateHandlebarOutput, @@ -204,6 +205,7 @@ index 123..456 100644 gitDiffResult, undefined, undefined, + undefined, { buildOutputGeneratorContext: mockBuildOutputGeneratorContext, generateHandlebarOutput: mockGenerateHandlebarOutput, diff --git a/tests/core/output/flagFullDirectoryStructure.test.ts b/tests/core/output/flagFullDirectoryStructure.test.ts index b2d70bc88..1990b445b 100644 --- a/tests/core/output/flagFullDirectoryStructure.test.ts +++ b/tests/core/output/flagFullDirectoryStructure.test.ts @@ -72,6 +72,7 @@ describe('includeFullDirectoryStructure flag', () => { undefined, undefined, undefined, + undefined, deps, ); @@ -105,6 +106,7 @@ describe('includeFullDirectoryStructure flag', () => { undefined, undefined, undefined, + undefined, deps, ); diff --git a/tests/core/output/outputGenerate.test.ts b/tests/core/output/outputGenerate.test.ts index 7f6a98430..7d93003d3 100644 --- a/tests/core/output/outputGenerate.test.ts +++ b/tests/core/output/outputGenerate.test.ts @@ -49,6 +49,7 @@ describe('outputGenerate', () => { undefined, undefined, undefined, + undefined, mockDeps, ); @@ -61,6 +62,7 @@ describe('outputGenerate', () => { undefined, undefined, undefined, + undefined, ); expect(output).toBe('mock output'); }); diff --git a/tests/core/output/outputGenerateDiffs.test.ts b/tests/core/output/outputGenerateDiffs.test.ts index c0f9b6601..79e989286 100644 --- a/tests/core/output/outputGenerateDiffs.test.ts +++ b/tests/core/output/outputGenerateDiffs.test.ts @@ -88,6 +88,7 @@ describe('Output Generation with Diffs', () => { gitDiffResult, undefined, undefined, + undefined, mockDeps, ); @@ -123,6 +124,7 @@ describe('Output Generation with Diffs', () => { undefined, undefined, undefined, + undefined, mockDeps, ); @@ -158,6 +160,7 @@ describe('Output Generation with Diffs', () => { undefined, undefined, undefined, + undefined, mockDeps, ); @@ -193,6 +196,7 @@ describe('Output Generation with Diffs', () => { undefined, undefined, undefined, + undefined, mockDeps, ); @@ -238,6 +242,7 @@ describe('Output Generation with Diffs', () => { undefined, undefined, undefined, + undefined, mockDeps, ); diff --git a/tests/core/packager.test.ts b/tests/core/packager.test.ts index 69138cc13..60fd55c21 100644 --- a/tests/core/packager.test.ts +++ b/tests/core/packager.test.ts @@ -99,6 +99,7 @@ describe('packager', () => { undefined, progressCallback, [{ rootLabel: 'root', files: mockFilePaths }], + [], ); expect(mockDeps.calculateMetrics).toHaveBeenCalledWith( mockProcessedFiles, diff --git a/tests/core/packager/produceOutput.test.ts b/tests/core/packager/produceOutput.test.ts index ac8e324dd..2ea552570 100644 --- a/tests/core/packager/produceOutput.test.ts +++ b/tests/core/packager/produceOutput.test.ts @@ -26,6 +26,7 @@ describe('produceOutput', () => { undefined, progressCallback, undefined, + undefined, mockDeps, ); @@ -37,6 +38,7 @@ describe('produceOutput', () => { undefined, undefined, undefined, + undefined, ); expect(mockDeps.writeOutputToDisk).toHaveBeenCalledWith('generated output', mockConfig); expect(mockDeps.copyToClipboardIfEnabled).toHaveBeenCalledWith('generated output', progressCallback, mockConfig); @@ -61,6 +63,7 @@ describe('produceOutput', () => { gitLogResult as Parameters[5], vi.fn(), undefined, + undefined, mockDeps, ); @@ -72,6 +75,7 @@ describe('produceOutput', () => { gitDiffResult, gitLogResult, undefined, + undefined, ); }); @@ -80,7 +84,18 @@ describe('produceOutput', () => { const mockConfig = createMockConfig(); const progressCallback = vi.fn(); - await produceOutput(['/root'], mockConfig, [], [], undefined, undefined, progressCallback, undefined, mockDeps); + await produceOutput( + ['/root'], + mockConfig, + [], + [], + undefined, + undefined, + progressCallback, + undefined, + undefined, + mockDeps, + ); expect(progressCallback).toHaveBeenCalledWith('Writing output file...'); }); @@ -116,6 +131,7 @@ describe('produceOutput', () => { undefined, progressCallback, undefined, + undefined, mockDeps, ); @@ -134,7 +150,18 @@ describe('produceOutput', () => { }); const progressCallback = vi.fn(); - await produceOutput(['/root'], mockConfig, [], [], undefined, undefined, progressCallback, undefined, mockDeps); + await produceOutput( + ['/root'], + mockConfig, + [], + [], + undefined, + undefined, + progressCallback, + undefined, + undefined, + mockDeps, + ); expect(progressCallback).toHaveBeenCalledWith('Writing output files...'); }); @@ -148,7 +175,7 @@ describe('produceOutput', () => { }, }); - await produceOutput(['/root'], mockConfig, [], [], undefined, undefined, vi.fn(), undefined, mockDeps); + await produceOutput(['/root'], mockConfig, [], [], undefined, undefined, vi.fn(), undefined, undefined, mockDeps); expect(mockDeps.copyToClipboardIfEnabled).not.toHaveBeenCalled(); }); diff --git a/tests/core/packager/splitOutput.test.ts b/tests/core/packager/splitOutput.test.ts index c1e92ba93..6fcc10cc5 100644 --- a/tests/core/packager/splitOutput.test.ts +++ b/tests/core/packager/splitOutput.test.ts @@ -66,6 +66,7 @@ describe('packager split output', () => { undefined, expect.any(Function), [{ rootLabel: 'root', files: allFilePaths }], + [], ); expect(calculateMetrics).toHaveBeenCalledWith(