fix: preserve canonical snippet names (#1380)
* fix: preserve canonical snippet names * test: cover same-directory snippet alias precedence --------- Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
This commit is contained in:
committed by
GitHub
co-authored by
Isaac Sanchez
parent
920f987393
commit
b764577943
@@ -85,27 +85,38 @@ function loadSnippetFile(dir, filename, source) {
|
||||
};
|
||||
}
|
||||
|
||||
function registerSnippet(registry, snippet) {
|
||||
function removeSnippetAliases(registry, snippet, canonicalNames) {
|
||||
for (const alias of snippet.aliases) {
|
||||
const aliasKey = alias.toLowerCase();
|
||||
if (canonicalNames.has(aliasKey)) continue;
|
||||
if (registry.get(aliasKey) === snippet) registry.delete(aliasKey);
|
||||
}
|
||||
}
|
||||
|
||||
function registerSnippet(registry, snippet, canonicalNames) {
|
||||
const key = snippet.name.toLowerCase();
|
||||
const existing = registry.get(key);
|
||||
if (existing) {
|
||||
for (const alias of existing.aliases) registry.delete(alias.toLowerCase());
|
||||
if (existing?.name.toLowerCase() === key) {
|
||||
removeSnippetAliases(registry, existing, canonicalNames);
|
||||
}
|
||||
registry.set(key, snippet);
|
||||
canonicalNames.add(key);
|
||||
for (const alias of snippet.aliases) {
|
||||
if (SNIPPET_NAME_PATTERN.test(alias)) registry.set(alias.toLowerCase(), snippet);
|
||||
const aliasKey = alias.toLowerCase();
|
||||
if (SNIPPET_NAME_PATTERN.test(alias) && !canonicalNames.has(aliasKey)) registry.set(aliasKey, snippet);
|
||||
}
|
||||
}
|
||||
|
||||
function loadSnippetRegistry(workingDirectory) {
|
||||
const registry = new Map();
|
||||
const canonicalNames = new Set();
|
||||
for (const { dir, source } of getLoadDirs(workingDirectory)) {
|
||||
if (!fs.existsSync(dir)) continue;
|
||||
for (const filename of fs.readdirSync(dir)) {
|
||||
if (!filename.endsWith(SNIPPET_EXTENSION)) continue;
|
||||
try {
|
||||
const snippet = loadSnippetFile(dir, filename, source);
|
||||
if (snippet) registerSnippet(registry, snippet);
|
||||
if (snippet) registerSnippet(registry, snippet, canonicalNames);
|
||||
} catch (error) {
|
||||
console.warn(`[Snippets] Failed to load ${path.join(dir, filename)}:`, error);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,35 @@ describe('snippets', () => {
|
||||
expect(getSnippet('same', projectDir)?.content).toBe('New');
|
||||
});
|
||||
|
||||
test('canonical snippet names take precedence over colliding aliases', () => {
|
||||
writeSnippet('.opencode/snippets/review.md', 'Review by name');
|
||||
writeSnippet('.opencode/snippet/helper.md', '---\naliases: [review, help]\n---\nReview by alias');
|
||||
|
||||
expect(getSnippet('review', projectDir)).toEqual(expect.objectContaining({ name: 'review', content: 'Review by name' }));
|
||||
expect(getSnippet('help', projectDir)).toEqual(expect.objectContaining({ name: 'helper', content: 'Review by alias' }));
|
||||
expect(expandSnippets('Use #review and #help', projectDir)).toBe('Use Review by name and Review by alias');
|
||||
});
|
||||
|
||||
test('canonical snippet names override earlier same-directory aliases', () => {
|
||||
writeSnippet('.opencode/snippet/alias-first.md', '---\naliases: [review, assist]\n---\nReview by alias');
|
||||
writeSnippet('.opencode/snippet/review.md', 'Review by name');
|
||||
|
||||
const snippetDir = path.join(projectDir, '.opencode', 'snippet');
|
||||
const originalReaddirSync = fs.readdirSync;
|
||||
fs.readdirSync = function readdirSync(dir, ...args) {
|
||||
if (dir === snippetDir) return ['alias-first.md', 'review.md'];
|
||||
return originalReaddirSync.call(fs, dir, ...args);
|
||||
};
|
||||
|
||||
try {
|
||||
expect(getSnippet('review', projectDir)).toEqual(expect.objectContaining({ name: 'review', content: 'Review by name' }));
|
||||
expect(getSnippet('assist', projectDir)).toEqual(expect.objectContaining({ name: 'alias-first', content: 'Review by alias' }));
|
||||
expect(expandSnippets('Use #review and #assist', projectDir)).toBe('Use Review by name and Review by alias');
|
||||
} finally {
|
||||
fs.readdirSync = originalReaddirSync;
|
||||
}
|
||||
});
|
||||
|
||||
test('creates updates and deletes snippets', () => {
|
||||
expect(createSnippet('custom-one', { content: 'Body', aliases: ['co'] }, projectDir, 'project')).toEqual(
|
||||
expect.objectContaining({ name: 'custom-one', content: 'Body', aliases: ['co'] }),
|
||||
|
||||
Reference in New Issue
Block a user