diff --git a/apps/blog-app/src/app/routes/blog.ts b/apps/blog-app/src/app/routes/blog.ts
index e3375905b..870a84122 100644
--- a/apps/blog-app/src/app/routes/blog.ts
+++ b/apps/blog-app/src/app/routes/blog.ts
@@ -15,7 +15,7 @@ export const routeMeta: RouteMeta = {
imports: [RouterOutlet, RouterLink, NgFor],
template: `
- {{ post.attributes.title }} |
+ {{ post.attributes.title }} |
About |
Contact
diff --git a/apps/docs-app/docs/features/routing/content.md b/apps/docs-app/docs/features/routing/content.md
index c48500f5c..ebe6fbba1 100644
--- a/apps/docs-app/docs/features/routing/content.md
+++ b/apps/docs-app/docs/features/routing/content.md
@@ -146,7 +146,7 @@ export interface PostAttributes {
template: `
-
-
+
{{ post.attributes.title }}
diff --git a/packages/content/src/lib/content-file.ts b/packages/content/src/lib/content-file.ts
index 475a76cce..2c89eeeba 100644
--- a/packages/content/src/lib/content-file.ts
+++ b/packages/content/src/lib/content-file.ts
@@ -2,6 +2,7 @@ export interface ContentFile<
Attributes extends Record = Record
> {
filename: string;
+ slug: string;
content?: string;
attributes: Attributes;
}
diff --git a/packages/content/src/lib/content-files-list-token.spec.ts b/packages/content/src/lib/content-files-list-token.spec.ts
index ac9294c3b..fa2c2d5cf 100644
--- a/packages/content/src/lib/content-files-list-token.spec.ts
+++ b/packages/content/src/lib/content-files-list-token.spec.ts
@@ -5,6 +5,9 @@ vi.mock('./get-content-files', () => {
return {
getContentFilesList: () => ({
'/test.md': { title: 'Test' },
+ '/path/to/test.md': { title: 'Test' },
+ '\\path\\to\\test.md': { title: 'Test' },
+ '/path/to/test with spaces.md': { title: 'Test' },
}),
};
});
@@ -14,9 +17,31 @@ describe('CONTENT_FILES_LIST_TOKEN', () => {
const firstParsedFile = CONTENT_FILES_LIST_TOKEN[0];
expect(CONTENT_FILES_LIST_TOKEN).toBeTruthy();
expect(firstParsedFile.filename).toEqual('/test.md');
+ expect(firstParsedFile.slug).toEqual('test');
expect(firstParsedFile.attributes['title']).toEqual('Test');
});
+ it('should extract the slug from nested path', () => {
+ const { CONTENT_FILES_LIST_TOKEN } = setup();
+ const firstParsedFile = CONTENT_FILES_LIST_TOKEN[1];
+ expect(CONTENT_FILES_LIST_TOKEN).toBeTruthy();
+ expect(firstParsedFile.slug).toEqual('test');
+ });
+
+ it('should extract the slug from nested path (windows)', () => {
+ const { CONTENT_FILES_LIST_TOKEN } = setup();
+ const firstParsedFile = CONTENT_FILES_LIST_TOKEN[2];
+ expect(CONTENT_FILES_LIST_TOKEN).toBeTruthy();
+ expect(firstParsedFile.slug).toEqual('test');
+ });
+
+ it('should extract the slug without spaces', () => {
+ const { CONTENT_FILES_LIST_TOKEN } = setup();
+ const firstParsedFile = CONTENT_FILES_LIST_TOKEN[3];
+ expect(CONTENT_FILES_LIST_TOKEN).toBeTruthy();
+ expect(firstParsedFile.slug).toEqual('test%20with%20spaces');
+ });
+
function setup() {
TestBed.configureTestingModule({});
return {
diff --git a/packages/content/src/lib/content-files-list-token.ts b/packages/content/src/lib/content-files-list-token.ts
index 7ec3d3e96..21c9ff25e 100644
--- a/packages/content/src/lib/content-files-list-token.ts
+++ b/packages/content/src/lib/content-files-list-token.ts
@@ -3,6 +3,11 @@ import { InjectionToken } from '@angular/core';
import { ContentFile } from './content-file';
import { getContentFilesList } from './get-content-files';
+function getSlug(filename: string) {
+ const parts = filename.match(/^(\\|\/)(.+(\\|\/))*(.+)\.(.+)$/);
+ return parts?.length ? parts[4] : '';
+}
+
export const CONTENT_FILES_LIST_TOKEN = new InjectionToken(
'@analogjs/content Content Files List',
{
@@ -16,6 +21,7 @@ export const CONTENT_FILES_LIST_TOKEN = new InjectionToken(
return {
filename,
attributes,
+ slug: encodeURI(getSlug(filename)),
};
});
},
diff --git a/packages/content/src/lib/content.spec.ts b/packages/content/src/lib/content.spec.ts
index 08dde5332..116da8c12 100644
--- a/packages/content/src/lib/content.spec.ts
+++ b/packages/content/src/lib/content.spec.ts
@@ -62,6 +62,7 @@ Test Content`),
expect(c.content).toMatch('Test Content');
expect(c.attributes).toEqual({ slug: 'test' });
expect(c.filename).toEqual('/src/content/test.md');
+ expect(c.slug).toEqual('test');
});
flushMicrotasks();
}));
@@ -90,6 +91,7 @@ Test Content`),
expect(c.content).toMatch('Test Content');
expect(c.attributes).toEqual({ slug: 'custom-slug-test' });
expect(c.filename).toEqual('/src/content/custom-slug-test.md');
+ expect(c.slug).toEqual('custom-slug-test');
});
flushMicrotasks();
}));
diff --git a/packages/content/src/lib/content.ts b/packages/content/src/lib/content.ts
index 16cce2859..0cbc4afd7 100644
--- a/packages/content/src/lib/content.ts
+++ b/packages/content/src/lib/content.ts
@@ -34,6 +34,7 @@ export function injectContent<
return of({
attributes: {},
filename,
+ slug: slug || '',
content: fallback,
});
}
@@ -56,6 +57,7 @@ export function injectContent<
return {
filename,
+ slug: slug || '',
attributes,
content,
};
diff --git a/packages/content/src/lib/inject-content-files.spec.ts b/packages/content/src/lib/inject-content-files.spec.ts
index 551e5562c..381bce248 100644
--- a/packages/content/src/lib/inject-content-files.spec.ts
+++ b/packages/content/src/lib/inject-content-files.spec.ts
@@ -15,6 +15,7 @@ describe('injectContentFiles', () => {
const contentFiles: ContentFile[] = [
{
filename: '/src/content/test.md',
+ slug: 'test',
attributes: {
testAttribute1: 'hello world',
testAttribute2: 2,