fix(ls): preserve qmd:/// collection aliases

This commit is contained in:
Tobi Lütke 2026-05-16 17:12:38 +00:00
parent 8fdc4815c5
commit 2dc8634ac7
No known key found for this signature in database
2 changed files with 80 additions and 8 deletions

View File

@ -1303,15 +1303,22 @@ function listFiles(pathArg?: string): void {
const match = allColls
.filter(c => normalized === c.name || normalized.startsWith(c.name + '/'))
.sort((a, b) => b.name.length - a.name.length)[0];
if (!match) {
console.error(`Collection not found for path: ${pathArg}`);
console.error(`Run 'qmd ls' to see available collections.`);
closeDb();
process.exit(1);
if (match) {
collectionName = match.name;
const rest = normalized.slice(match.name.length).replace(/^\//, '');
pathPrefix = rest || null;
} else {
// Preserve the historical qmd:////collection/path alias behavior for normal
// collections when no absolute-path collection matches.
const parsed = parseVirtualPath(pathArg);
if (!parsed) {
console.error(`Invalid virtual path: ${pathArg}`);
closeDb();
process.exit(1);
}
collectionName = parsed.collectionName;
pathPrefix = parsed.path;
}
collectionName = match.name;
const rest = normalized.slice(match.name.length).replace(/^\//, '');
pathPrefix = rest || null;
} else if (afterScheme !== null) {
// Normal virtual path: qmd://collection-name/path
const parsed = parseVirtualPath(pathArg);

View File

@ -863,6 +863,71 @@ describe("CLI ls Command", () => {
expect(stdout).toContain("qmd://fixtures/docs/api.md");
});
test("continues to normalize extra slashes for normal collection virtual paths", async () => {
const { stdout, stderr, exitCode } = await runQmd(["ls", "qmd:///fixtures/docs"], { dbPath: localDbPath });
expect(stderr).toBe("");
expect(exitCode).toBe(0);
expect(stdout).toContain("qmd://fixtures/docs/api.md");
});
test("lists an absolute-path collection from a qmd:/// virtual path", async () => {
const env = await createIsolatedTestEnv("absolute-qmd-path");
const absoluteDir = await mkdtemp(join(tmpdir(), "qmd-absolute-collection-"));
await writeFile(join(absoluteDir, "root.md"), "# Absolute collection\n");
await writeFile(
join(env.configDir, "index.yml"),
`collections:\n "${absoluteDir}":\n path: "${absoluteDir}"\n pattern: "**/*.md"\n`
);
const update = await runQmd(["update"], {
cwd: absoluteDir,
dbPath: env.dbPath,
configDir: env.configDir,
});
expect(update.exitCode).toBe(0);
const { stdout, stderr, exitCode } = await runQmd(["ls", `qmd://${absoluteDir}/`], {
cwd: absoluteDir,
dbPath: env.dbPath,
configDir: env.configDir,
});
expect(stderr).toBe("");
expect(exitCode).toBe(0);
expect(stdout).toContain(`qmd://${absoluteDir}/root.md`);
});
test("lists an absolute-path collection from a raw path using the longest prefix match", async () => {
const env = await createIsolatedTestEnv("absolute-raw-path");
const parentCollectionName = await mkdtemp(join(tmpdir(), "qmd-absolute-parent-name-"));
const childCollectionName = join(parentCollectionName, "nested");
const parentDataDir = await mkdtemp(join(tmpdir(), "qmd-absolute-parent-data-"));
const childDataDir = await mkdtemp(join(tmpdir(), "qmd-absolute-child-data-"));
await writeFile(join(parentDataDir, "parent.md"), "# Parent collection\n");
await writeFile(join(childDataDir, "child.md"), "# Child collection\n");
await writeFile(
join(env.configDir, "index.yml"),
`collections:\n "${parentCollectionName}":\n path: "${parentDataDir}"\n pattern: "**/*.md"\n "${childCollectionName}":\n path: "${childDataDir}"\n pattern: "**/*.md"\n`
);
const update = await runQmd(["update"], {
cwd: parentDataDir,
dbPath: env.dbPath,
configDir: env.configDir,
});
expect(update.exitCode).toBe(0);
const { stdout, stderr, exitCode } = await runQmd(["ls", `${childCollectionName}/`], {
cwd: childDataDir,
dbPath: env.dbPath,
configDir: env.configDir,
});
expect(stderr).toBe("");
expect(exitCode).toBe(0);
expect(stdout).toContain(`qmd://${childCollectionName}/child.md`);
expect(stdout).not.toContain("No files found");
expect(stdout).not.toContain(`qmd://${parentCollectionName}/parent.md`);
});
test("handles non-existent collection", async () => {
const { stderr, exitCode } = await runQmd(["ls", "nonexistent"], { dbPath: localDbPath });
expect(exitCode).toBe(1);