fix(memory): ignore @ inside code blocks (#10201)

This commit is contained in:
Allen Hutchison
2025-09-30 18:24:33 -07:00
committed by GitHub
parent c913ce3c0b
commit f207ea94d6
2 changed files with 41 additions and 23 deletions

View File

@@ -423,6 +423,31 @@ describe('memoryImportProcessor', () => {
);
});
it('should not process imports in repeated inline code blocks', async () => {
const content = '`@noimport` and `@noimport`';
const projectRoot = testPath('test', 'project');
const basePath = testPath(projectRoot, 'src');
const result = await processImports(
content,
basePath,
true,
undefined,
projectRoot,
);
expect(result.content).toBe(content);
});
it('should not import when @ is inside an inline code block', async () => {
const content =
'We should not ` @import` when the symbol is inside an inline code string.';
const testRootDir = testPath('test', 'project');
const result = await processImports(content, testRootDir);
expect(result.content).toBe(content);
expect(result.importTree.imports).toBeUndefined();
});
it('should allow imports from parent and subdirectories within project root', async () => {
const content =
'Parent import: @../parent.md Subdir import: @./components/sub.md';

View File

@@ -7,7 +7,7 @@
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { isSubpath } from './paths.js';
import { marked } from 'marked';
import { marked, type Token } from 'marked';
// Simple console logger for import processing
const logger = {
@@ -153,39 +153,32 @@ function isLetter(char: string): boolean {
function findCodeRegions(content: string): Array<[number, number]> {
const regions: Array<[number, number]> = [];
const tokens = marked.lexer(content);
let offset = 0;
// Map from raw content to a queue of its start indices in the original content.
const rawContentIndices = new Map<string, number[]>();
function walk(token: { type: string; raw: string; tokens?: unknown[] }) {
function walk(token: Token, baseOffset: number) {
if (token.type === 'code' || token.type === 'codespan') {
if (!rawContentIndices.has(token.raw)) {
const indices: number[] = [];
let lastIndex = -1;
while ((lastIndex = content.indexOf(token.raw, lastIndex + 1)) !== -1) {
indices.push(lastIndex);
}
rawContentIndices.set(token.raw, indices);
}
const indices = rawContentIndices.get(token.raw);
if (indices && indices.length > 0) {
// Assume tokens are processed in order of appearance.
// Dequeue the next available index for this raw content.
const idx = indices.shift()!;
regions.push([idx, idx + token.raw.length]);
}
regions.push([baseOffset, baseOffset + token.raw.length]);
}
if ('tokens' in token && token.tokens) {
let childOffset = 0;
for (const child of token.tokens) {
walk(child as { type: string; raw: string; tokens?: unknown[] });
const childIndexInParent = token.raw.indexOf(child.raw, childOffset);
if (childIndexInParent === -1) {
logger.error(
`Could not find child token in parent raw content. Aborting parsing for this branch. Child raw: "${child.raw}"`,
);
break;
}
walk(child, baseOffset + childIndexInParent);
childOffset = childIndexInParent + child.raw.length;
}
}
}
for (const token of tokens) {
walk(token);
walk(token, offset);
offset += token.raw.length;
}
return regions;