Skip to content

Commit

Permalink
feat(content): introduce build-time rendering/highlighting for markdo…
Browse files Browse the repository at this point in the history
…wn (#1174)
  • Loading branch information
brandonroberts authored Jun 21, 2024
1 parent e029a28 commit 9ccf6b2
Show file tree
Hide file tree
Showing 28 changed files with 390 additions and 289 deletions.
10 changes: 4 additions & 6 deletions apps/blog-app/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ export const appConfig: ApplicationConfig = {
provideHttpClient(),
provideClientHydration(),
provideContent(
withMarkdownRenderer({ loadMermaid: () => import('mermaid') }),
withShikiHighlighter({
highlighter: {
additionalLangs: ['mermaid'],
},
})
withMarkdownRenderer({
loadMermaid: () => import('mermaid'),
}),
withShikiHighlighter()
),
provideFileRouter(
withInMemoryScrolling({ anchorScrolling: 'enabled' }),
Expand Down
8 changes: 8 additions & 0 deletions apps/blog-app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export default defineConfig(() => {
plugins: [
analog({
static: true,
content: {
highlighter: 'shiki',
shikiOptions: {
highlighter: {
additionalLangs: ['mermaid'],
},
},
},
prerender: {
routes: async () => {
return [
Expand Down
107 changes: 48 additions & 59 deletions apps/docs-app/docs/features/routing/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,33 +108,64 @@ export const appConfig: ApplicationConfig = {
};
```

To enable build-time syntax highlighting with `shiki`, configure the `analog` plugin in the `vite.config.ts`.

```ts
import { defineConfig } from 'vite';
import analog from '@analogjs/platform';

export default defineConfig({
// ...
plugins: [
analog({
content: {
highlighter: 'shiki',
},
}),
],
});
```

#### Configure Shiki Highlighter

> Please check out [Shiki Documentation](https://shiki.style/) for more information on configuring Shiki.
To configure Shiki, you can pass a `WithShikiHighlighterOptions` object to the `withShikiHighlighter()` function.
To configure Shiki, you can pass options to the `shikiOptions` object.

```ts
import { withShikiHighlighter } from '@analogjs/content/shiki-highlighter';

provideContent(
withMarkdownRenderer(),
withShikiHighlighter({
highlight: { theme: 'nord' },
})
);
import { defineConfig } from 'vite';
import analog from '@analogjs/platform';

export default defineConfig({
// ...
plugins: [
analog({
content: {
highlighter: 'shiki',
shikiOptions: {
highlight: {
// alternate theme
theme: 'ayu-dark'
}
highlighter: {
// add more languages
additionalLangs: ['mermaid'],
},
},
},
}),
],
});
```

By default, `withShikiHighlighter` has the following options.
By default, `shikiOptions` has the following options.

```json
```ts
{
"container": "%s",
"highlight": {
"themes": {
"dark": "github-dark",
"light": "github-light"
}
},
"theme": "github-dark"
}
"highlighter": {
"langs": [
"json",
Expand All @@ -145,55 +176,13 @@ By default, `withShikiHighlighter` has the following options.
"html",
"css",
"angular-html",
"angular-ts"
"angular-ts",
],
"themes": ["github-dark", "github-light"]
}
}
```

Provided options will be merged **shallowly**. For example:

```ts
import { withShikiHighlighter } from '@analogjs/content/shiki-highlighter';

withShikiHighlighter({
highlighter: {
// langs will be provied by the default options
themes: ['ayu-dark'], // only ayu-dark will be bundled
},
highlight: {
theme: 'ayu-dark', // use ayu-dark as the theme
// theme: 'dark-plus' // ERROR: dark-plus is not bundled
},
});
```

### Custom Syntax Highlighter

If you want to use a custom syntax highlighter, you can use the `withHighlighter()` function to provide a custom highlighter.

```ts
import { withHighlighter, MarkedContentHighlighter } from '@analogjs/content';
// NOTE: make sure to install 'marked-highlight' if not already installed
import { markedHighlight } from 'marked-highlight';

class CustomHighlighter extends MarkedContentHighlighter {
override getHighlightExtension() {
return markedHighlight({
highlight: (code, lang) => {
return 'your custom highlight';
},
});
}
}

provideContent(
withMarkdownRenderer(),
withHighlighter({ useClass: CustomHighlighter })
);
```

## Defining Content Files

For more flexibility, markdown content files can be provided in the `src/content` folder. Here you can list markdown files such as blog posts.
Expand Down
9 changes: 3 additions & 6 deletions packages/content/prism-highlighter/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { withHighlighter } from '@analogjs/content';
import { ContentRenderer, NoopContentRenderer } from '@analogjs/content';
import { Provider } from '@angular/core';
import { PrismHighlighter } from './lib/prism-highlighter';

export { PrismHighlighter };

export function withPrismHighlighter(): Provider {
return withHighlighter({ useClass: PrismHighlighter });
export function withPrismHighlighter(): Provider[] {
return [{ provide: ContentRenderer, useClass: NoopContentRenderer }];
}

This file was deleted.

70 changes: 24 additions & 46 deletions packages/content/shiki-highlighter/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,32 @@
import { withHighlighter } from '@analogjs/content';
import { ContentRenderer, NoopContentRenderer } from '@analogjs/content';
import { Provider } from '@angular/core';
import type { BundledLanguage } from 'shiki';
import {
defaultHighlighterOptions,
SHIKI_CONTAINER_OPTION,
SHIKI_HIGHLIGHT_OPTIONS,
SHIKI_HIGHLIGHTER_OPTIONS,
ShikiHighlighter,
type ShikiHighlighterOptions,
type ShikiHighlightOptions,
} from './lib/shiki-highlighter';
import type {
BundledLanguage,
BundledTheme,
CodeOptionsMeta,
CodeOptionsMultipleThemes,
CodeOptionsSingleTheme,
CodeToHastOptionsCommon,
} from 'shiki';

export { ShikiHighlighter };
export type ShikiHighlightOptions = Partial<
Omit<CodeToHastOptionsCommon<BundledLanguage>, 'lang'>
> &
CodeOptionsMeta &
Partial<CodeOptionsSingleTheme<BundledTheme>> &
Partial<CodeOptionsMultipleThemes<BundledTheme>>;

export interface WithShikiHighlighterOptions {
highlighter?: Partial<ShikiHighlighterOptions> & {
additionalLangs?: BundledLanguage[];
};
highlight?: ShikiHighlightOptions;
export type WithShikiHighlighterOptions = ShikiHighlightOptions & {
container?: string;
}

export function withShikiHighlighter({
highlighter = {},
highlight = {},
container = '%s',
}: WithShikiHighlighterOptions = {}): Provider {
if (!highlighter.themes) {
if (highlight.theme) {
highlighter.themes = [highlight.theme];
} else if (highlight.themes && typeof highlight.themes === 'object') {
highlighter.themes = Object.values(highlight.themes) as string[];
} else {
highlighter.themes = defaultHighlighterOptions.themes;
}
}

if (!highlighter.langs) {
highlighter.langs = defaultHighlighterOptions.langs;
}

if (highlighter.additionalLangs) {
highlighter.langs.push(...highlighter.additionalLangs);
delete highlighter.additionalLangs;
}
};

export function withShikiHighlighter(
_opts: WithShikiHighlighterOptions = {}
): Provider[] {
return [
{ provide: SHIKI_HIGHLIGHTER_OPTIONS, useValue: highlighter },
{ provide: SHIKI_HIGHLIGHT_OPTIONS, useValue: highlight },
{ provide: SHIKI_CONTAINER_OPTION, useValue: container },
withHighlighter({ useClass: ShikiHighlighter }),
{
provide: ContentRenderer,
useClass: NoopContentRenderer,
},
];
}

This file was deleted.

6 changes: 3 additions & 3 deletions packages/content/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
export { AnchorNavigationDirective } from './lib/anchor-navigation.directive';
export { injectContent } from './lib/content';
export { ContentFile } from './lib/content-file';
export { ContentRenderer } from './lib/content-renderer';
export { ContentRenderer, NoopContentRenderer } from './lib/content-renderer';
export { injectContentFiles } from './lib/inject-content-files';
export { MarkdownContentRendererService } from './lib/markdown-content-renderer.service';
export {
MarkdownContentRendererService,
provideContent,
withMarkdownRenderer,
MERMAID_IMPORT_TOKEN,
} from './lib/markdown-content-renderer.service';
} from './lib/provide-content';
export { default as MarkdownRouteComponent } from './lib/markdown-route.component';
export { default as MarkdownComponent } from './lib/markdown.component';
export { parseRawContentFile } from './lib/parse-raw-content-file';
Expand Down
Loading

0 comments on commit 9ccf6b2

Please sign in to comment.