Skip to content

Commit 21b26a4

Browse files
authored
Merge pull request #1563 from QwenLM/fix/issue-1549-skip-enoent-import
fix: skip non-existent file imports instead of warning (ENOENT)
2 parents ae9ba8b + 1562780 commit 21b26a4

File tree

2 files changed

+97
-6
lines changed

2 files changed

+97
-6
lines changed

packages/core/src/utils/memoryImportProcessor.test.ts

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,15 @@ const findCodeBlocks = (
9292

9393
describe('memoryImportProcessor', () => {
9494
beforeEach(() => {
95-
vi.clearAllMocks();
95+
vi.resetAllMocks(); // Use resetAllMocks to clear mock implementations
9696
// Mock console methods
9797
console.warn = vi.fn();
9898
console.error = vi.fn();
9999
console.debug = vi.fn();
100+
// Default mock for lstat (used by findProjectRoot)
101+
mockedFs.lstat.mockRejectedValue(
102+
Object.assign(new Error('ENOENT'), { code: 'ENOENT' }),
103+
);
100104
});
101105

102106
afterEach(() => {
@@ -204,20 +208,43 @@ describe('memoryImportProcessor', () => {
204208
);
205209
});
206210

207-
it('should handle file not found errors', async () => {
211+
it('should silently preserve content when file not found (ENOENT)', async () => {
208212
const content = 'Content @./nonexistent.md more content';
209213
const basePath = testPath('test', 'path');
210214

211-
mockedFs.access.mockRejectedValue(new Error('File not found'));
215+
// Mock ENOENT error (file not found)
216+
mockedFs.access.mockRejectedValue(
217+
Object.assign(new Error('ENOENT: no such file or directory'), {
218+
code: 'ENOENT',
219+
}),
220+
);
221+
222+
const result = await processImports(content, basePath, true);
223+
224+
// Content should be preserved as-is when file doesn't exist
225+
expect(result.content).toBe(content);
226+
// No error should be logged for ENOENT
227+
expect(console.error).not.toHaveBeenCalled();
228+
});
229+
230+
it('should log error for non-ENOENT file access errors', async () => {
231+
const content = 'Content @./permission-denied.md more content';
232+
const basePath = testPath('test', 'path');
233+
234+
// Mock a permission denied error (not ENOENT)
235+
mockedFs.access.mockRejectedValue(
236+
Object.assign(new Error('Permission denied'), { code: 'EACCES' }),
237+
);
212238

213239
const result = await processImports(content, basePath, true);
214240

241+
// Should show error comment for non-ENOENT errors
215242
expect(result.content).toContain(
216-
'<!-- Import failed: ./nonexistent.md - File not found -->',
243+
'<!-- Import failed: ./permission-denied.md - Permission denied -->',
217244
);
218245
expect(console.error).toHaveBeenCalledWith(
219246
'[ERROR] [ImportProcessor]',
220-
'Failed to import ./nonexistent.md: File not found',
247+
'Failed to import ./permission-denied.md: Permission denied',
221248
);
222249
});
223250

@@ -448,6 +475,50 @@ describe('memoryImportProcessor', () => {
448475
expect(result.importTree.imports).toBeUndefined();
449476
});
450477

478+
it('should still import valid paths while ignoring non-existent paths', async () => {
479+
const content = '使用 @./valid.md 文件和 @中文路径 注解';
480+
const basePath = testPath('test', 'path');
481+
const importedContent = 'Valid imported content';
482+
483+
// Mock: valid.md exists, 中文路径 doesn't exist
484+
mockedFs.access
485+
.mockResolvedValueOnce(undefined) // ./valid.md exists
486+
.mockRejectedValueOnce(
487+
Object.assign(new Error('ENOENT'), { code: 'ENOENT' }),
488+
); // 中文路径 doesn't exist
489+
mockedFs.readFile.mockResolvedValue(importedContent);
490+
491+
const result = await processImports(content, basePath, true);
492+
493+
// Should import valid.md
494+
expect(result.content).toContain(importedContent);
495+
expect(result.content).toContain('<!-- Imported from: ./valid.md -->');
496+
// The non-existent path should remain as-is
497+
expect(result.content).toContain('@中文路径');
498+
});
499+
500+
it('should import Chinese file names if they exist', async () => {
501+
const content = '导入 @./中文文档.md 文件';
502+
const projectRoot = testPath('test', 'project');
503+
const basePath = testPath(projectRoot, 'src');
504+
const importedContent = '这是中文文档的内容';
505+
506+
mockedFs.access.mockResolvedValue(undefined);
507+
mockedFs.readFile.mockResolvedValue(importedContent);
508+
509+
const result = await processImports(
510+
content,
511+
basePath,
512+
true,
513+
undefined,
514+
projectRoot,
515+
);
516+
517+
// Should successfully import the Chinese-named file
518+
expect(result.content).toContain(importedContent);
519+
expect(result.content).toContain('<!-- Imported from: ./中文文档.md -->');
520+
});
521+
451522
it('should allow imports from parent and subdirectories within project root', async () => {
452523
const content =
453524
'Parent import: @../parent.md Subdir import: @./components/sub.md';

packages/core/src/utils/memoryImportProcessor.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@ function isLetter(char: string): boolean {
150150
); // a-z
151151
}
152152

153+
/**
154+
* Checks if an error is a "file not found" error (ENOENT)
155+
*/
156+
function isFileNotFoundError(err: unknown): boolean {
157+
return (
158+
typeof err === 'object' &&
159+
err !== null &&
160+
'code' in err &&
161+
(err as { code: unknown }).code === 'ENOENT'
162+
);
163+
}
164+
153165
function findCodeRegions(content: string): Array<[number, number]> {
154166
const regions: Array<[number, number]> = [];
155167
const tokens = marked.lexer(content);
@@ -292,7 +304,9 @@ export async function processImports(
292304
depth + 1,
293305
);
294306
} catch (error) {
295-
if (debugMode) {
307+
// If file doesn't exist, silently skip this import (it's not a real import)
308+
// Only log warnings for other types of errors
309+
if (!isFileNotFoundError(error) && debugMode) {
296310
logger.warn(
297311
`Failed to import ${fullPath}: ${hasMessage(error) ? error.message : 'Unknown error'}`,
298312
);
@@ -371,6 +385,12 @@ export async function processImports(
371385
result += `<!-- Imported from: ${importPath} -->\n${imported.content}\n<!-- End of import from: ${importPath} -->`;
372386
imports.push(imported.importTree);
373387
} catch (err: unknown) {
388+
// If file doesn't exist, preserve the original @path text (it's not a real import)
389+
if (isFileNotFoundError(err)) {
390+
result += `@${importPath}`;
391+
continue;
392+
}
393+
// For other errors, log and add error comment
374394
let message = 'Unknown error';
375395
if (hasMessage(err)) {
376396
message = err.message;

0 commit comments

Comments
 (0)