Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/true-buses-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': patch
---

Fixes Starlight Markdown processing being potentially applied to files that should not be processed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { describe, expect, test } from 'vitest';
import {
getMarkdownProcessorPaths,
shouldTransformPath,
} from '../../integrations/markdown-process';
import { createPluginTestOptions } from '../test-utils';

const root = new URL('file:///path/to/project/');
const srcDir = new URL('./src/', root);

describe('getMarkdownProcessorPaths', () => {
test('returns allowed directory paths with trailing slashes', async () => {
const allowedPaths = await getAllowedPaths(['./src/content/comments']);

expect(allowedPaths).toEqual([
'/path/to/project/src/content/docs/',
'/path/to/project/src/content/comments/',
]);
});
});

describe('shouldTransformPath', () => {
const allowedPaths = [
'/path/to/project/src/content/docs/',
'/path/to/project/src/content/comments/',
];

test('transforms files in allowed paths', () => {
expect(shouldTransformPath('/path/to/project/src/content/docs/index.md', allowedPaths)).toBe(
true
);
expect(
shouldTransformPath('/path/to/project/src/content/comments/index.md', allowedPaths)
).toBe(true);
});

test('does not transform sibling paths sharing the same prefix of allowed paths', () => {
expect(
shouldTransformPath('/path/to/project/src/content/docs-test/index.md', allowedPaths)
).toBe(false);
expect(
shouldTransformPath('/path/to/project/src/content/comments-test/index.md', allowedPaths)
).toBe(false);
});
});

async function getAllowedPaths(processedDirs: string[] = []) {
const options = await createPluginTestOptions({ title: 'Test', markdown: { processedDirs } });

return getMarkdownProcessorPaths({ ...options, astroConfig: { root, srcDir } });
}
16 changes: 14 additions & 2 deletions packages/starlight/integrations/markdown-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { AstroConfig } from 'astro';
import { resolveCollectionPath } from '../utils/collection-fs';
import { ensureTrailingSlash } from '../utils/path';
import type { HookParameters, StarlightConfig } from '../types';

/**
Expand All @@ -21,10 +22,12 @@ export interface MarkdownProcessorPluginOptions {
* utility to determine if a file should be transformed by a plugin or not.
*/
export function getMarkdownProcessorPaths(options: MarkdownProcessorPluginOptions): string[] {
const paths = [normalizePath(resolveCollectionPath('docs', options.astroConfig.srcDir))];
const paths = [normalizeDirectoryPath(resolveCollectionPath('docs', options.astroConfig.srcDir))];

for (const processedDir of options.starlightConfig.markdown.processedDirs) {
paths.push(normalizePath(resolve(fileURLToPath(options.astroConfig.root), processedDir)));
paths.push(
normalizeDirectoryPath(resolve(fileURLToPath(options.astroConfig.root), processedDir))
);
}

return paths;
Expand All @@ -50,3 +53,12 @@ const backSlashRegex = /\\/g;
function normalizePath(path: string) {
return path.replace(backSlashRegex, '/');
}

/**
* Allowed paths are directory prefixes compared with `startsWith()` in {@link shouldTransformPath},
* so we ensure they have a trailing slash to avoid matching sibling directories with the same
* prefix.
*/
function normalizeDirectoryPath(path: string) {
return ensureTrailingSlash(normalizePath(path));
}