@astrojs/starlight 0.36.3 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/index.ts +17 -21
- package/integrations/asides.ts +4 -22
- package/integrations/code-rtl-support.ts +9 -14
- package/integrations/heading-links.ts +6 -42
- package/integrations/remark-rehype.ts +126 -0
- package/integrations/vite-layer-order.ts +66 -0
- package/package.json +2 -1
- package/style/props.css +3 -3
- package/style/reset.css +1 -1
- package/user-components/Tabs.astro +3 -3
- package/utils/user-config.ts +13 -0
- package/integrations/remark-rehype-utils.ts +0 -38
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# @astrojs/starlight
|
|
2
2
|
|
|
3
|
+
## 0.37.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#3491](https://github.com/withastro/starlight/pull/3491) [`28810f0`](https://github.com/withastro/starlight/commit/28810f085faf017f3fedd1407e741bdf6c232848) Thanks [@JusticeMatthew](https://github.com/JusticeMatthew)! - Changes text overflow styling in Markdown content
|
|
8
|
+
|
|
9
|
+
⚠️ **Potentially breaking change:** This release switches the [`overflow-wrap`](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap) CSS style for common elements to `break-word`. In most cases, there should be little visual impact, but this change can impact how layouts with implicit sizing (such as tables) look, improving legibility in how words wrap.
|
|
10
|
+
|
|
11
|
+
If you want to preserve the previous styling, you can add the following [custom CSS](https://starlight.astro.build/guides/css-and-tailwind/#custom-css-styles) to your site:
|
|
12
|
+
|
|
13
|
+
```css
|
|
14
|
+
p, h1, h2, h3, h4, h5, h6, code {
|
|
15
|
+
overflow-wrap: anywhere;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- [#3351](https://github.com/withastro/starlight/pull/3351) [`239698c`](https://github.com/withastro/starlight/commit/239698c53625f5411792e314994d20c20f9ede77) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Ensures that Starlight CSS layer order is predictable in custom pages using the `<StarlightPage>` component.
|
|
20
|
+
|
|
21
|
+
Previously, due to how [import order](https://docs.astro.build/en/guides/styling/#import-order) works in Astro, the `<StarlightPage>` component had to be the first import in custom pages to set up [cascade layers](https://starlight.astro.build/guides/css-and-tailwind/#cascade-layers) used internally by Starlight to manage the order of its styles.
|
|
22
|
+
|
|
23
|
+
With this change, this restriction no longer applies and Starlight’s styles will be applied correctly regardless of the import order of the `<StarlightPage>` component.
|
|
24
|
+
|
|
25
|
+
- [#3521](https://github.com/withastro/starlight/pull/3521) [`ca7b771`](https://github.com/withastro/starlight/commit/ca7b771e5bd4da3fe500bbad562e69d5880690ea) Thanks [@shubham-padia](https://github.com/shubham-padia)! - Fixes an issue where a vertical scrollbar could be displayed on the Starlight `<Tabs>` component when zooming the page
|
|
26
|
+
|
|
27
|
+
⚠️ **Potentially breaking change:** The `<Tabs>` component no longer uses `margin-bottom` and `border-bottom` to highlight the current tab. This is now done with a `box-shadow`. If you have custom styling for your tabs, you may need to update it.
|
|
28
|
+
|
|
29
|
+
If you want to preserve the previous styling, you can add the following custom CSS to your site:
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
starlight-tabs .tab {
|
|
33
|
+
margin-bottom: -2px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
starlight-tabs .tab > [role='tab'] {
|
|
37
|
+
border-bottom: 2px solid var(--sl-color-gray-5);
|
|
38
|
+
box-shadow: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
starlight-tabs .tab [role='tab'][aria-selected='true'] {
|
|
42
|
+
border-color: var(--sl-color-text-accent);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- [#3549](https://github.com/withastro/starlight/pull/3549) [`1cf50eb`](https://github.com/withastro/starlight/commit/1cf50ebb18c0232be581cf0aff0c192e4c421e55) Thanks [@jacobdalamb](https://github.com/jacobdalamb)! - Updates the default sans-serif system font stack, dropping support for the `-apple-system` and `BlinkMacSystemFont` font names used in older browsers. These are no longer needed in [browsers officially supported by Starlight](https://browsersl.ist/#q=%3E+0.5%25%2C+not+dead%2C+Chrome+%3E%3D+105%2C+Edge+%3E%3D+105%2C+Firefox+%3E%3D+121%2C+Safari+%3E%3D+15.4%2C+iOS+%3E%3D+15.4%2C+not+op_mini+all).
|
|
47
|
+
|
|
48
|
+
If you still need to support older browsers, you can add the following custom CSS to your site:
|
|
49
|
+
|
|
50
|
+
```css
|
|
51
|
+
:root {
|
|
52
|
+
--sl-font-system: ui-sans-serif, system-ui, -apple-system,
|
|
53
|
+
BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
|
54
|
+
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
|
55
|
+
'Segoe UI Symbol', 'Noto Color Emoji';
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- [#3332](https://github.com/withastro/starlight/pull/3332) [`f61f99d`](https://github.com/withastro/starlight/commit/f61f99dc09c59d26761ffebc611969e20b866191) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds a new [`markdown.processedDirs`](https://starlight.astro.build/reference/configuration/#processeddirs) configuration option to specify additional directories where files should be processed by Starlight’s Markdown pipeline.
|
|
60
|
+
|
|
61
|
+
By default, Starlight’s processing only applies to Markdown and MDX content loaded using Starlight’s `docsLoader()`. This new option allows to extend this processing to other directories, which can be useful if you are rendering content from a custom content collection using the `<StarlightPage>` component and expect Starlight’s Markdown processing to be applied to that content as well.
|
|
62
|
+
|
|
3
63
|
## 0.36.3
|
|
4
64
|
|
|
5
65
|
### Patch Changes
|
package/index.ts
CHANGED
|
@@ -13,11 +13,16 @@ import { AstroError } from 'astro/errors';
|
|
|
13
13
|
import { spawn } from 'node:child_process';
|
|
14
14
|
import { dirname, relative } from 'node:path';
|
|
15
15
|
import { fileURLToPath } from 'node:url';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
starlightRehypePlugins,
|
|
18
|
+
starlightRemarkPlugins,
|
|
19
|
+
type RemarkRehypePluginOptions,
|
|
20
|
+
} from './integrations/remark-rehype';
|
|
21
|
+
import { starlightDirectivesRestorationIntegration } from './integrations/asides';
|
|
17
22
|
import { starlightExpressiveCode } from './integrations/expressive-code/index';
|
|
18
23
|
import { starlightSitemap } from './integrations/sitemap';
|
|
24
|
+
import { vitePluginStarlightCssLayerOrder } from './integrations/vite-layer-order';
|
|
19
25
|
import { vitePluginStarlightUserConfig } from './integrations/virtual-user-config';
|
|
20
|
-
import { rehypeRtlCodeSupport } from './integrations/code-rtl-support';
|
|
21
26
|
import {
|
|
22
27
|
injectPluginTranslationsTypes,
|
|
23
28
|
runPlugins,
|
|
@@ -26,7 +31,6 @@ import {
|
|
|
26
31
|
} from './utils/plugins';
|
|
27
32
|
import { processI18nConfig } from './utils/i18n';
|
|
28
33
|
import type { StarlightConfig } from './types';
|
|
29
|
-
import { starlightAutolinkHeadings } from './integrations/heading-links';
|
|
30
34
|
|
|
31
35
|
export default function StarlightIntegration(
|
|
32
36
|
userOpts: StarlightUserConfigWithPlugins
|
|
@@ -115,31 +119,23 @@ export default function StarlightIntegration(
|
|
|
115
119
|
const selfIndex = config.integrations.findIndex((i) => i.name === '@astrojs/starlight');
|
|
116
120
|
config.integrations.splice(selfIndex + 1, 0, ...integrations);
|
|
117
121
|
|
|
122
|
+
const remarkRehypeOptions: RemarkRehypePluginOptions = {
|
|
123
|
+
starlightConfig,
|
|
124
|
+
astroConfig: config,
|
|
125
|
+
useTranslations,
|
|
126
|
+
absolutePathToLang,
|
|
127
|
+
};
|
|
128
|
+
|
|
118
129
|
updateConfig({
|
|
119
130
|
vite: {
|
|
120
131
|
plugins: [
|
|
132
|
+
vitePluginStarlightCssLayerOrder(),
|
|
121
133
|
vitePluginStarlightUserConfig(command, starlightConfig, config, pluginTranslations),
|
|
122
134
|
],
|
|
123
135
|
},
|
|
124
136
|
markdown: {
|
|
125
|
-
remarkPlugins: [
|
|
126
|
-
|
|
127
|
-
starlightConfig,
|
|
128
|
-
astroConfig: config,
|
|
129
|
-
useTranslations,
|
|
130
|
-
absolutePathToLang,
|
|
131
|
-
}),
|
|
132
|
-
],
|
|
133
|
-
rehypePlugins: [
|
|
134
|
-
rehypeRtlCodeSupport({ astroConfig: config }),
|
|
135
|
-
// Process headings and add anchor links.
|
|
136
|
-
...starlightAutolinkHeadings({
|
|
137
|
-
starlightConfig,
|
|
138
|
-
astroConfig: config,
|
|
139
|
-
useTranslations,
|
|
140
|
-
absolutePathToLang,
|
|
141
|
-
}),
|
|
142
|
-
],
|
|
137
|
+
remarkPlugins: [...starlightRemarkPlugins(remarkRehypeOptions)],
|
|
138
|
+
rehypePlugins: [...starlightRehypePlugins(remarkRehypeOptions)],
|
|
143
139
|
},
|
|
144
140
|
scopedStyleStrategy: 'where',
|
|
145
141
|
// If not already configured, default to prefetching all links on hover.
|
package/integrations/asides.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="mdast-util-directive" />
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { AstroIntegration } from 'astro';
|
|
4
4
|
import { h as _h, s as _s, type Properties, type Result } from 'hastscript';
|
|
5
5
|
import type { Node, Paragraph as P, Parent, PhrasingContent, Root } from 'mdast';
|
|
6
6
|
import {
|
|
@@ -11,23 +11,15 @@ import {
|
|
|
11
11
|
} from 'mdast-util-directive';
|
|
12
12
|
import { toMarkdown } from 'mdast-util-to-markdown';
|
|
13
13
|
import { toString } from 'mdast-util-to-string';
|
|
14
|
-
import remarkDirective from 'remark-directive';
|
|
15
14
|
import type { Plugin, Transformer } from 'unified';
|
|
16
15
|
import { visit } from 'unist-util-visit';
|
|
17
|
-
import type {
|
|
18
|
-
import {
|
|
16
|
+
import type { RemarkRehypePluginOptions } from './remark-rehype';
|
|
17
|
+
import type { StarlightIcon } from '../types';
|
|
19
18
|
import { Icons } from '../components/Icons';
|
|
20
19
|
import { fromHtml } from 'hast-util-from-html';
|
|
21
20
|
import type { Element } from 'hast';
|
|
22
21
|
import { throwInvalidAsideIconError } from './asides-error';
|
|
23
22
|
|
|
24
|
-
interface AsidesOptions {
|
|
25
|
-
starlightConfig: Pick<StarlightConfig, 'defaultLocale' | 'locales'>;
|
|
26
|
-
astroConfig: { root: AstroConfig['root']; srcDir: AstroConfig['srcDir'] };
|
|
27
|
-
useTranslations: HookParameters<'config:setup'>['useTranslations'];
|
|
28
|
-
absolutePathToLang: HookParameters<'config:setup'>['absolutePathToLang'];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
23
|
/** Hacky function that generates an mdast HTML tree ready for conversion to HTML by rehype. */
|
|
32
24
|
function h(el: string, attrs: Properties = {}, children: unknown[] = []): P {
|
|
33
25
|
const { tagName, properties } = _h(el, attrs);
|
|
@@ -132,7 +124,7 @@ function makeSvgChildNodes(children: Result['children']): P[] {
|
|
|
132
124
|
* </aside>
|
|
133
125
|
* ```
|
|
134
126
|
*/
|
|
135
|
-
function remarkAsides(options:
|
|
127
|
+
export function remarkAsides(options: RemarkRehypePluginOptions): Plugin<[], Root> {
|
|
136
128
|
type Variant = 'note' | 'tip' | 'caution' | 'danger';
|
|
137
129
|
const variants = new Set(['note', 'tip', 'caution', 'danger']);
|
|
138
130
|
const isAsideVariant = (s: string): s is Variant => variants.has(s);
|
|
@@ -169,11 +161,7 @@ function remarkAsides(options: AsidesOptions): Plugin<[], Root> {
|
|
|
169
161
|
],
|
|
170
162
|
};
|
|
171
163
|
|
|
172
|
-
const docsCollectionPath = getRemarkRehypeDocsCollectionPath(options.astroConfig.srcDir);
|
|
173
|
-
|
|
174
164
|
const transformer: Transformer<Root> = (tree, file) => {
|
|
175
|
-
if (!shouldTransformFile(file, docsCollectionPath)) return;
|
|
176
|
-
|
|
177
165
|
const lang = options.absolutePathToLang(file.path);
|
|
178
166
|
const t = options.useTranslations(lang);
|
|
179
167
|
visit(tree, (node, index, parent) => {
|
|
@@ -253,12 +241,6 @@ function remarkAsides(options: AsidesOptions): Plugin<[], Root> {
|
|
|
253
241
|
};
|
|
254
242
|
}
|
|
255
243
|
|
|
256
|
-
type RemarkPlugins = NonNullable<NonNullable<AstroUserConfig['markdown']>['remarkPlugins']>;
|
|
257
|
-
|
|
258
|
-
export function starlightAsides(options: AsidesOptions): RemarkPlugins {
|
|
259
|
-
return [remarkDirective, remarkAsides(options)];
|
|
260
|
-
}
|
|
261
|
-
|
|
262
244
|
export function remarkDirectivesRestoration() {
|
|
263
245
|
return function transformer(tree: Root) {
|
|
264
246
|
visit(tree, (node, index, parent) => {
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import type { AstroConfig } from 'astro';
|
|
2
1
|
import type { Root } from 'hast';
|
|
3
2
|
import { CONTINUE, SKIP, visit } from 'unist-util-visit';
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
interface RtlCodeSupportOptions {
|
|
8
|
-
astroConfig: Pick<AstroConfig, 'srcDir'>;
|
|
9
|
-
}
|
|
3
|
+
import type { Transformer } from 'unified';
|
|
4
|
+
import type { RemarkRehypePluginOptions } from './remark-rehype';
|
|
10
5
|
|
|
11
6
|
/**
|
|
12
7
|
* rehype plugin that adds `dir` attributes to `<code>` and `<pre>`
|
|
@@ -22,13 +17,9 @@ interface RtlCodeSupportOptions {
|
|
|
22
17
|
* - `<code>` is often LTR, but could also be RTL. `dir="auto"` ensures the bidirectional
|
|
23
18
|
* algorithm treats the contents of `<code>` in isolation and gives its best guess.
|
|
24
19
|
*/
|
|
25
|
-
export function rehypeRtlCodeSupport(
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
return () => (root: Root, file: VFile) => {
|
|
29
|
-
if (!shouldTransformFile(file, docsCollectionPath)) return;
|
|
30
|
-
|
|
31
|
-
visit(root, 'element', (el) => {
|
|
20
|
+
export function rehypeRtlCodeSupport(_options: RemarkRehypePluginOptions) {
|
|
21
|
+
const transformer: Transformer<Root> = (tree) => {
|
|
22
|
+
visit(tree, 'element', (el) => {
|
|
32
23
|
if (el.tagName === 'pre' || el.tagName === 'code') {
|
|
33
24
|
el.properties ||= {};
|
|
34
25
|
if (!('dir' in el.properties)) {
|
|
@@ -39,4 +30,8 @@ export function rehypeRtlCodeSupport({ astroConfig }: RtlCodeSupportOptions) {
|
|
|
39
30
|
return CONTINUE;
|
|
40
31
|
});
|
|
41
32
|
};
|
|
33
|
+
|
|
34
|
+
return function attacher() {
|
|
35
|
+
return transformer;
|
|
36
|
+
};
|
|
42
37
|
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
|
|
2
|
-
import type { AstroConfig, AstroUserConfig } from 'astro';
|
|
3
1
|
import type { Nodes, Root } from 'hast';
|
|
4
2
|
import { toString } from 'hast-util-to-string';
|
|
5
3
|
import { h } from 'hastscript';
|
|
6
4
|
import type { Transformer } from 'unified';
|
|
7
5
|
import { SKIP, visit } from 'unist-util-visit';
|
|
8
|
-
import type {
|
|
9
|
-
import { getRemarkRehypeDocsCollectionPath, shouldTransformFile } from './remark-rehype-utils';
|
|
6
|
+
import type { RemarkRehypePluginOptions } from './remark-rehype';
|
|
10
7
|
|
|
11
8
|
const AnchorLinkIcon = h(
|
|
12
9
|
'span',
|
|
@@ -24,16 +21,13 @@ const AnchorLinkIcon = h(
|
|
|
24
21
|
/**
|
|
25
22
|
* Add anchor links to headings.
|
|
26
23
|
*/
|
|
27
|
-
export default function rehypeAutolinkHeadings(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
) {
|
|
24
|
+
export default function rehypeAutolinkHeadings({
|
|
25
|
+
absolutePathToLang,
|
|
26
|
+
useTranslations,
|
|
27
|
+
}: RemarkRehypePluginOptions) {
|
|
32
28
|
const transformer: Transformer<Root> = (tree, file) => {
|
|
33
|
-
if (!shouldTransformFile(file, docsCollectionPath)) return;
|
|
34
|
-
|
|
35
29
|
const pageLang = absolutePathToLang(file.path);
|
|
36
|
-
const t =
|
|
30
|
+
const t = useTranslations(pageLang);
|
|
37
31
|
|
|
38
32
|
visit(tree, 'element', function (node, index, parent) {
|
|
39
33
|
if (!headingRank(node) || !node.properties.id || typeof index !== 'number' || !parent) {
|
|
@@ -69,36 +63,6 @@ export default function rehypeAutolinkHeadings(
|
|
|
69
63
|
};
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
interface AutolinkHeadingsOptions {
|
|
73
|
-
starlightConfig: Pick<StarlightConfig, 'markdown'>;
|
|
74
|
-
astroConfig: Pick<AstroConfig, 'srcDir'> & {
|
|
75
|
-
experimental: Pick<AstroConfig['experimental'], 'headingIdCompat'>;
|
|
76
|
-
};
|
|
77
|
-
useTranslations: HookParameters<'config:setup'>['useTranslations'];
|
|
78
|
-
absolutePathToLang: HookParameters<'config:setup'>['absolutePathToLang'];
|
|
79
|
-
}
|
|
80
|
-
type RehypePlugins = NonNullable<NonNullable<AstroUserConfig['markdown']>['rehypePlugins']>;
|
|
81
|
-
|
|
82
|
-
export const starlightAutolinkHeadings = ({
|
|
83
|
-
starlightConfig,
|
|
84
|
-
astroConfig,
|
|
85
|
-
useTranslations,
|
|
86
|
-
absolutePathToLang,
|
|
87
|
-
}: AutolinkHeadingsOptions): RehypePlugins =>
|
|
88
|
-
starlightConfig.markdown.headingLinks
|
|
89
|
-
? [
|
|
90
|
-
[
|
|
91
|
-
rehypeHeadingIds,
|
|
92
|
-
{ experimentalHeadingIdCompat: astroConfig.experimental?.headingIdCompat },
|
|
93
|
-
],
|
|
94
|
-
rehypeAutolinkHeadings(
|
|
95
|
-
getRemarkRehypeDocsCollectionPath(astroConfig.srcDir),
|
|
96
|
-
useTranslations,
|
|
97
|
-
absolutePathToLang
|
|
98
|
-
),
|
|
99
|
-
]
|
|
100
|
-
: [];
|
|
101
|
-
|
|
102
66
|
// This utility is inlined from https://github.com/syntax-tree/hast-util-heading-rank
|
|
103
67
|
// Copyright (c) 2020 Titus Wormer <tituswormer@gmail.com>
|
|
104
68
|
// MIT License: https://github.com/syntax-tree/hast-util-heading-rank/blob/main/license
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import type { AstroConfig } from 'astro';
|
|
4
|
+
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
|
|
5
|
+
import type { Root as RehypeRoot } from 'hast';
|
|
6
|
+
import type { Root as RemarkRoot } from 'mdast';
|
|
7
|
+
import remarkDirective from 'remark-directive';
|
|
8
|
+
import type { Plugin } from 'unified';
|
|
9
|
+
import type { VFile } from 'vfile';
|
|
10
|
+
import { resolveCollectionPath } from '../utils/collection-fs';
|
|
11
|
+
import type { HookParameters, StarlightConfig } from '../types';
|
|
12
|
+
import { remarkAsides } from './asides';
|
|
13
|
+
import { rehypeRtlCodeSupport } from './code-rtl-support';
|
|
14
|
+
import rehypeAutolinkHeadings from './heading-links';
|
|
15
|
+
|
|
16
|
+
/** List of remark plugins to apply. */
|
|
17
|
+
export function starlightRemarkPlugins(options: RemarkRehypePluginOptions): RemarkPlugin[] {
|
|
18
|
+
return [remarkDirective, remarkPlugins(options)];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** List of rehype plugins to apply. */
|
|
22
|
+
export function starlightRehypePlugins(options: RemarkRehypePluginOptions): RehypePlugin[] {
|
|
23
|
+
return [
|
|
24
|
+
...(options.starlightConfig.markdown.headingLinks
|
|
25
|
+
? [
|
|
26
|
+
[
|
|
27
|
+
rehypeHeadingIds,
|
|
28
|
+
{ experimentalHeadingIdCompat: options.astroConfig.experimental?.headingIdCompat },
|
|
29
|
+
],
|
|
30
|
+
]
|
|
31
|
+
: []),
|
|
32
|
+
rehypePlugins(options),
|
|
33
|
+
] as RehypePlugin[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Remark plugin applying other Starlight remark plugins if necessary. */
|
|
37
|
+
function remarkPlugins(options: RemarkRehypePluginOptions): RemarkPlugin {
|
|
38
|
+
const remarkRehypePaths = getRemarkRehypePaths(options);
|
|
39
|
+
|
|
40
|
+
return function attacher(this) {
|
|
41
|
+
const remarkAsidesTransformer = remarkAsides(options).call(this)!;
|
|
42
|
+
|
|
43
|
+
return async function transformer(...args) {
|
|
44
|
+
const [, file] = args;
|
|
45
|
+
|
|
46
|
+
if (!shouldTransformFile(file, remarkRehypePaths)) return;
|
|
47
|
+
|
|
48
|
+
await remarkAsidesTransformer(...args);
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Rehype plugin applying other Starlight rehype plugins if necessary. */
|
|
54
|
+
function rehypePlugins(options: RemarkRehypePluginOptions): RehypePlugin {
|
|
55
|
+
const remarkRehypePaths = getRemarkRehypePaths(options);
|
|
56
|
+
|
|
57
|
+
return function attacher(this) {
|
|
58
|
+
const rehypeRtlCodeSupportTransformer = rehypeRtlCodeSupport(options).call(this);
|
|
59
|
+
const rehypeAutolinkHeadingsTransformer = rehypeAutolinkHeadings(options).call(this);
|
|
60
|
+
|
|
61
|
+
return async function transformer(...args) {
|
|
62
|
+
const [, file] = args;
|
|
63
|
+
|
|
64
|
+
if (!shouldTransformFile(file, remarkRehypePaths)) return;
|
|
65
|
+
|
|
66
|
+
await rehypeRtlCodeSupportTransformer(...args);
|
|
67
|
+
|
|
68
|
+
if (options.starlightConfig.markdown.headingLinks) {
|
|
69
|
+
await rehypeAutolinkHeadingsTransformer(...args);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns the paths to the Starlight docs collection and any additional paths defined in the
|
|
77
|
+
* `starlightConfig.markdown.processedDirs` option that can be used with the
|
|
78
|
+
* `shouldTransformFile()` utility to determine if a file should be transformed by a plugin or not.
|
|
79
|
+
*/
|
|
80
|
+
function getRemarkRehypePaths(options: RemarkRehypePluginOptions): string[] {
|
|
81
|
+
const paths = [normalizePath(resolveCollectionPath('docs', options.astroConfig.srcDir))];
|
|
82
|
+
|
|
83
|
+
for (const processedDir of options.starlightConfig.markdown.processedDirs) {
|
|
84
|
+
paths.push(normalizePath(resolve(fileURLToPath(options.astroConfig.root), processedDir)));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return paths;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Determines if a file should be transformed by a remark/rehype plugin, e.g. files without a known
|
|
92
|
+
* path or files that are not part of the allowed remark/rehype paths are skipped.
|
|
93
|
+
*/
|
|
94
|
+
function shouldTransformFile(file: VFile, remarkRehypePaths: string[]) {
|
|
95
|
+
// If the content is rendered using the content loader `renderMarkdown()` API, a file path
|
|
96
|
+
// is not provided.
|
|
97
|
+
// In that case, we skip the file.
|
|
98
|
+
if (!file?.path) return false;
|
|
99
|
+
|
|
100
|
+
const normalizedPath = normalizePath(file.path);
|
|
101
|
+
|
|
102
|
+
// If the document is not part of the allowed remark/rehype paths, skip it.
|
|
103
|
+
return remarkRehypePaths.some((path) => normalizedPath.startsWith(path));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* File path separators seems to be inconsistent on Windows between remark/rehype plugins used on
|
|
108
|
+
* Markdown vs MDX files.
|
|
109
|
+
* For the time being, we normalize all paths to unix style paths.
|
|
110
|
+
*/
|
|
111
|
+
const backSlashRegex = /\\/g;
|
|
112
|
+
function normalizePath(path: string) {
|
|
113
|
+
return path.replace(backSlashRegex, '/');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface RemarkRehypePluginOptions {
|
|
117
|
+
starlightConfig: Pick<StarlightConfig, 'defaultLocale' | 'locales' | 'markdown'>;
|
|
118
|
+
astroConfig: Pick<AstroConfig, 'root' | 'srcDir'> & {
|
|
119
|
+
experimental: Pick<AstroConfig['experimental'], 'headingIdCompat'>;
|
|
120
|
+
};
|
|
121
|
+
useTranslations: HookParameters<'config:setup'>['useTranslations'];
|
|
122
|
+
absolutePathToLang: HookParameters<'config:setup'>['absolutePathToLang'];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
type RemarkPlugin = Plugin<[], RemarkRoot>;
|
|
126
|
+
type RehypePlugin = Plugin<[], RehypeRoot>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ViteUserConfig } from 'astro';
|
|
2
|
+
import MagicString from 'magic-string';
|
|
3
|
+
|
|
4
|
+
const starlightPageImportSource = '@astrojs/starlight/components/StarlightPage.astro';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Vite plugin that ensures the StarlightPage component is always imported first when imported in
|
|
8
|
+
* an Astro file.
|
|
9
|
+
*
|
|
10
|
+
* This is necessary to ensure a predictable CSS layer order which is defined by the `<Page />`
|
|
11
|
+
* imported by the `<StarlightPage />` component. If a user imports any other component using
|
|
12
|
+
* cascade layers before the `<StarlightPage />` component, it will result in undesired layers
|
|
13
|
+
* being created before we explicitly set the expected layer order.
|
|
14
|
+
*/
|
|
15
|
+
export function vitePluginStarlightCssLayerOrder(): VitePlugin {
|
|
16
|
+
return {
|
|
17
|
+
name: 'vite-plugin-starlight-css-layer-order',
|
|
18
|
+
enforce: 'pre',
|
|
19
|
+
transform(code, id) {
|
|
20
|
+
if (
|
|
21
|
+
!id.endsWith('.astro') ||
|
|
22
|
+
id.endsWith(starlightPageImportSource) ||
|
|
23
|
+
code.indexOf('StarlightPage.astro') === -1
|
|
24
|
+
) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let ast: ReturnType<typeof this.parse>;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
ast = this.parse(code);
|
|
32
|
+
} catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let hasStarlightPageImport = false;
|
|
37
|
+
|
|
38
|
+
for (const node of ast.body) {
|
|
39
|
+
if (node.type !== 'ImportDeclaration') continue;
|
|
40
|
+
if (node.source.value !== starlightPageImportSource) continue;
|
|
41
|
+
|
|
42
|
+
const importDefaultSpecifier = node.specifiers.find(
|
|
43
|
+
(specifier) => specifier.type === 'ImportDefaultSpecifier'
|
|
44
|
+
);
|
|
45
|
+
if (!importDefaultSpecifier) continue;
|
|
46
|
+
|
|
47
|
+
hasStarlightPageImport = true;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!hasStarlightPageImport) return;
|
|
52
|
+
|
|
53
|
+
// Format path to unix style path.
|
|
54
|
+
const filename = id.replace(/\\/g, '/');
|
|
55
|
+
const ms = new MagicString(code, { filename });
|
|
56
|
+
ms.prepend(`import "${starlightPageImportSource}";\n`);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
code: ms.toString(),
|
|
60
|
+
map: ms.generateMap({ hires: 'boundary' }),
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
type VitePlugin = NonNullable<ViteUserConfig['plugins']>[number];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/starlight",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Build beautiful, high-performance documentation websites with Astro",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docs",
|
|
@@ -204,6 +204,7 @@
|
|
|
204
204
|
"i18next": "^23.11.5",
|
|
205
205
|
"js-yaml": "^4.1.0",
|
|
206
206
|
"klona": "^2.0.6",
|
|
207
|
+
"magic-string": "^0.30.17",
|
|
207
208
|
"mdast-util-directive": "^3.0.0",
|
|
208
209
|
"mdast-util-to-markdown": "^2.1.0",
|
|
209
210
|
"mdast-util-to-string": "^4.0.0",
|
package/style/props.css
CHANGED
|
@@ -84,9 +84,9 @@
|
|
|
84
84
|
--sl-line-height: 1.75;
|
|
85
85
|
--sl-line-height-headings: 1.2;
|
|
86
86
|
|
|
87
|
-
--sl-font-system: ui-sans-serif, system-ui,
|
|
88
|
-
|
|
89
|
-
'
|
|
87
|
+
--sl-font-system: ui-sans-serif, system-ui, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
|
88
|
+
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
|
89
|
+
'Noto Color Emoji';
|
|
90
90
|
--sl-font-system-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
|
91
91
|
'Courier New', monospace;
|
|
92
92
|
--__sl-font: var(--sl-font, var(--sl-font-system)), var(--sl-font-system);
|
package/style/reset.css
CHANGED
|
@@ -116,23 +116,23 @@ if (isSynced) {
|
|
|
116
116
|
|
|
117
117
|
.tab {
|
|
118
118
|
display: flex;
|
|
119
|
-
margin-bottom: -2px;
|
|
120
119
|
}
|
|
121
120
|
.tab > [role='tab'] {
|
|
121
|
+
--sl-tab-color-border: var(--sl-color-gray-5);
|
|
122
122
|
display: flex;
|
|
123
123
|
align-items: center;
|
|
124
124
|
gap: 0.5rem;
|
|
125
125
|
line-height: var(--sl-line-height-headings);
|
|
126
126
|
padding: 0.275rem 1.25rem;
|
|
127
127
|
text-decoration: none;
|
|
128
|
-
|
|
128
|
+
box-shadow: 0 2px 0 var(--sl-tab-color-border);
|
|
129
129
|
color: var(--sl-color-gray-3);
|
|
130
130
|
outline-offset: var(--sl-outline-offset-inside);
|
|
131
131
|
overflow-wrap: initial;
|
|
132
132
|
}
|
|
133
133
|
.tab [role='tab'][aria-selected='true'] {
|
|
134
|
+
--sl-tab-color-border: var(--sl-color-text-accent);
|
|
134
135
|
color: var(--sl-color-white);
|
|
135
|
-
border-color: var(--sl-color-text-accent);
|
|
136
136
|
font-weight: 600;
|
|
137
137
|
}
|
|
138
138
|
|
package/utils/user-config.ts
CHANGED
|
@@ -272,6 +272,19 @@ const UserConfigSchema = z.object({
|
|
|
272
272
|
.describe(
|
|
273
273
|
'Define whether headings in content should be rendered with clickable anchor links. Default: `true`.'
|
|
274
274
|
),
|
|
275
|
+
/**
|
|
276
|
+
* Define additional directories where files should be processed by Starlight’s Markdown pipeline.
|
|
277
|
+
*
|
|
278
|
+
* Supports local directories relative to the root of your project, e.g. './src/data/comments/'.
|
|
279
|
+
* Content of the `docs` content collection is always processed by Starlight’s Markdown pipeline.
|
|
280
|
+
*/
|
|
281
|
+
processedDirs: z
|
|
282
|
+
.string()
|
|
283
|
+
.array()
|
|
284
|
+
.default([])
|
|
285
|
+
.describe(
|
|
286
|
+
'Define additional directories where files should be processed by Starlight’s Markdown pipeline. Default: `[]`.'
|
|
287
|
+
),
|
|
275
288
|
})
|
|
276
289
|
.default({})
|
|
277
290
|
.describe('Configure features that impact Starlight’s Markdown processing.'),
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { AstroConfig } from 'astro';
|
|
2
|
-
import type { VFile } from 'vfile';
|
|
3
|
-
import { resolveCollectionPath } from '../utils/collection-fs';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Returns the path to the Starlight docs collection ready to be used in remark/rehype plugins,
|
|
7
|
-
* e.g. with the `shouldTransformFile()` utility to determine if a file should be transformed
|
|
8
|
-
* by a plugin or not.
|
|
9
|
-
*/
|
|
10
|
-
export function getRemarkRehypeDocsCollectionPath(srcDir: AstroConfig['srcDir']) {
|
|
11
|
-
return normalizePath(resolveCollectionPath('docs', srcDir));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Determines if a file should be transformed by a remark/rehype plugin, e.g. files without a known
|
|
16
|
-
* path or files that are not part of the Starlight docs collection should be skipped.
|
|
17
|
-
*/
|
|
18
|
-
export function shouldTransformFile(file: VFile, docsCollectionPath: string) {
|
|
19
|
-
// If the content is rendered using the content loader `renderMarkdown()` API, a file path
|
|
20
|
-
// is not provided.
|
|
21
|
-
// In that case, we skip the file.
|
|
22
|
-
if (!file?.path) return false;
|
|
23
|
-
|
|
24
|
-
// If the document is not part of the Starlight docs collection, skip it.
|
|
25
|
-
if (!normalizePath(file.path).startsWith(docsCollectionPath)) return false;
|
|
26
|
-
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* File path separators seems to be inconsistent on Windows between remark/rehype plugins used on
|
|
32
|
-
* Markdown vs MDX files.
|
|
33
|
-
* For the time being, we normalize all paths to unix style paths.
|
|
34
|
-
*/
|
|
35
|
-
const backSlashRegex = /\\/g;
|
|
36
|
-
function normalizePath(path: string) {
|
|
37
|
-
return path.replace(backSlashRegex, '/');
|
|
38
|
-
}
|