@astrojs/starlight 0.16.0 → 0.17.1

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # @astrojs/starlight
2
2
 
3
+ ## 0.17.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1437](https://github.com/withastro/starlight/pull/1437) [`655aed4`](https://github.com/withastro/starlight/commit/655aed4840cae59e9abd64b4b585e60f1cfab209) Thanks [@hippotastic](https://github.com/hippotastic)! - Adds Starlight-specific types to `defineEcConfig` function and exports `StarlightExpressiveCodeOptions`.
8
+
9
+ This provides Starlight types and IntelliSense support for your Expressive Code configuration options inside an `ec.config.mjs` file. See the [Expressive Code documentation](https://expressive-code.com/key-features/code-component/#using-an-ecconfigmjs-file) for more information.
10
+
11
+ - [#1420](https://github.com/withastro/starlight/pull/1420) [`275f87f`](https://github.com/withastro/starlight/commit/275f87fd7fc676b9ab323354078c06894e0832c7) Thanks [@abdelhalimjean](https://github.com/abdelhalimjean)! - Fix rare `font-family` issue if users have a font installed with a name of `""`
12
+
13
+ - [#1365](https://github.com/withastro/starlight/pull/1365) [`a0af7cc`](https://github.com/withastro/starlight/commit/a0af7cc696da987a76edab96cdd2329779e87724) Thanks [@kevinzunigacuellar](https://github.com/kevinzunigacuellar)! - Correctly format Pagefind search result links when `trailingSlash: 'never'` is used
14
+
15
+ ## 0.17.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [#1389](https://github.com/withastro/starlight/pull/1389) [`21b3620`](https://github.com/withastro/starlight/commit/21b36201aa1e01c8395d0f24b2fa4e32b90550bb) Thanks [@connor-baer](https://github.com/connor-baer)! - Adds new `disable404Route` config option to disable injection of Astro’s default 404 route
20
+
21
+ - [#1395](https://github.com/withastro/starlight/pull/1395) [`ce05dfb`](https://github.com/withastro/starlight/commit/ce05dfb4b1e9b90fad057d5d4328e4445f986b3b) Thanks [@hippotastic](https://github.com/hippotastic)! - Adds a new [`<Code>` component](https://starlight.astro.build/guides/components/#code) to render dynamic code strings with Expressive Code
22
+
3
23
  ## 0.16.0
4
24
 
5
25
  ### Minor Changes
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import '@pagefind/default-ui/css/ui.css';
3
3
  import Icon from '../user-components/Icon.astro';
4
+ import project from 'virtual:starlight/project-context';
4
5
  import type { Props } from '../props';
5
6
 
6
7
  const { labels } = Astro.props;
@@ -15,7 +16,10 @@ const pagefindTranslations = {
15
16
  };
16
17
  ---
17
18
 
18
- <site-search data-translations={JSON.stringify(pagefindTranslations)}>
19
+ <site-search
20
+ data-translations={JSON.stringify(pagefindTranslations)}
21
+ data-strip-trailing-slash={project.trailingSlash === 'never'}
22
+ >
19
23
  <button data-open-modal disabled>
20
24
  {
21
25
  /* The span is `aria-hidden` because it is not shown on small screens. Instead, the icon label is used for accessibility purposes. */
@@ -112,10 +116,15 @@ const pagefindTranslations = {
112
116
  translations = JSON.parse(this.dataset.translations || '{}');
113
117
  } catch {}
114
118
 
119
+ const shouldStrip = this.dataset.stripTrailingSlash !== undefined;
120
+ const stripTrailingSlash = (path: string) => path.replace(/(.)\/(#.*)?$/, '$1$2');
121
+ const formatURL = shouldStrip ? stripTrailingSlash : (path: string) => path;
122
+
115
123
  window.addEventListener('DOMContentLoaded', () => {
116
124
  if (import.meta.env.DEV) return;
117
125
  const onIdle = window.requestIdleCallback || ((cb) => setTimeout(cb, 1));
118
126
  onIdle(async () => {
127
+ // @ts-expect-error — Missing types for @pagefind/default-ui package.
119
128
  const { PagefindUI } = await import('@pagefind/default-ui');
120
129
  new PagefindUI({
121
130
  element: '#starlight__search',
@@ -124,6 +133,13 @@ const pagefindTranslations = {
124
133
  showImages: false,
125
134
  translations,
126
135
  showSubResults: true,
136
+ processResult: (result: { url: string; sub_results: Array<{ url: string }> }) => {
137
+ result.url = formatURL(result.url);
138
+ result.sub_results = result.sub_results.map((sub_result) => {
139
+ sub_result.url = formatURL(sub_result.url);
140
+ return sub_result;
141
+ });
142
+ },
127
143
  });
128
144
  });
129
145
  });
package/components.ts CHANGED
@@ -4,3 +4,4 @@ export { default as Icon } from './user-components/Icon.astro';
4
4
  export { default as Tabs } from './user-components/Tabs.astro';
5
5
  export { default as TabItem } from './user-components/TabItem.astro';
6
6
  export { default as LinkCard } from './user-components/LinkCard.astro';
7
+ export { Code } from 'astro-expressive-code/components';
package/index.ts CHANGED
@@ -39,10 +39,12 @@ export default function StarlightIntegration({
39
39
 
40
40
  const useTranslations = createTranslationSystemFromFs(starlightConfig, config);
41
41
 
42
- injectRoute({
43
- pattern: '404',
44
- entrypoint: '@astrojs/starlight/404.astro',
45
- });
42
+ if (!userConfig.disable404Route) {
43
+ injectRoute({
44
+ pattern: '404',
45
+ entrypoint: '@astrojs/starlight/404.astro',
46
+ });
47
+ }
46
48
  injectRoute({
47
49
  pattern: '[...slug]',
48
50
  entrypoint: '@astrojs/starlight/index.astro',
@@ -51,9 +53,7 @@ export default function StarlightIntegration({
51
53
  // config or by a plugin.
52
54
  const allIntegrations = [...config.integrations, ...integrations];
53
55
  if (!allIntegrations.find(({ name }) => name === 'astro-expressive-code')) {
54
- integrations.push(
55
- ...starlightExpressiveCode({ starlightConfig, astroConfig: config, useTranslations })
56
- );
56
+ integrations.push(...starlightExpressiveCode({ starlightConfig, useTranslations }));
57
57
  }
58
58
  if (!allIntegrations.find(({ name }) => name === '@astrojs/sitemap')) {
59
59
  integrations.push(starlightSitemap(starlightConfig));
@@ -34,3 +34,37 @@
34
34
  */
35
35
 
36
36
  export * from 'astro-expressive-code';
37
+
38
+ import type { StarlightExpressiveCodeOptions } from './index';
39
+
40
+ export type { StarlightExpressiveCodeOptions };
41
+
42
+ /**
43
+ * A utility function that helps you define an Expressive Code configuration object. It is meant
44
+ * to be used inside the optional config file `ec.config.mjs` located in the root directory
45
+ * of your Starlight project, and its return value to be exported as the default export.
46
+ *
47
+ * Expressive Code will automatically detect this file and use the exported configuration object
48
+ * to override its own default settings.
49
+ *
50
+ * Using this function is recommended, but not required. It just passes through the given object,
51
+ * but it also provides type information for your editor's auto-completion and type checking.
52
+ *
53
+ * @example
54
+ * ```js
55
+ * // ec.config.mjs
56
+ * import { defineEcConfig } from '@astrojs/starlight/expressive-code'
57
+ *
58
+ * export default defineEcConfig({
59
+ * themes: ['starlight-dark', 'github-light'],
60
+ * styleOverrides: {
61
+ * borderRadius: '0.5rem',
62
+ * },
63
+ * })
64
+ * ```
65
+ */
66
+ export function defineEcConfig(config: StarlightExpressiveCodeOptions) {
67
+ return config;
68
+ }
69
+
70
+ export { getStarlightEcConfigPreprocessor } from './index';
@@ -2,8 +2,9 @@ import {
2
2
  astroExpressiveCode,
3
3
  type AstroExpressiveCodeOptions,
4
4
  addClassName,
5
+ type CustomConfigPreprocessors,
5
6
  } from 'astro-expressive-code';
6
- import type { AstroConfig, AstroIntegration } from 'astro';
7
+ import type { AstroIntegration } from 'astro';
7
8
  import type { StarlightConfig } from '../../types';
8
9
  import type { createTranslationSystemFromFs } from '../../utils/translations-fs';
9
10
  import { pathToLocale } from '../shared/pathToLocale';
@@ -60,56 +61,59 @@ export type StarlightExpressiveCodeOptions = Omit<AstroExpressiveCodeOptions, 't
60
61
  useStarlightUiThemeColors?: boolean | undefined;
61
62
  };
62
63
 
63
- export const starlightExpressiveCode = ({
64
- astroConfig,
64
+ type StarlightEcIntegrationOptions = {
65
+ starlightConfig: StarlightConfig;
66
+ useTranslations?: ReturnType<typeof createTranslationSystemFromFs> | undefined;
67
+ };
68
+
69
+ /**
70
+ * Create an Expressive Code configuration preprocessor based on Starlight config.
71
+ * Used internally to set up Expressive Code and by the `<Code>` component.
72
+ */
73
+ export function getStarlightEcConfigPreprocessor({
65
74
  starlightConfig,
66
75
  useTranslations,
67
- }: {
68
- astroConfig: Pick<AstroConfig, 'root' | 'srcDir'>;
69
- starlightConfig: StarlightConfig;
70
- useTranslations: ReturnType<typeof createTranslationSystemFromFs>;
71
- }): AstroIntegration[] => {
72
- const { locales, expressiveCode } = starlightConfig;
73
- if (expressiveCode === false) return [];
74
- const config: StarlightExpressiveCodeOptions =
75
- typeof expressiveCode === 'object' ? expressiveCode : {};
76
+ }: StarlightEcIntegrationOptions): CustomConfigPreprocessors['preprocessAstroIntegrationConfig'] {
77
+ return (input): AstroExpressiveCodeOptions => {
78
+ const astroConfig = input.astroConfig;
79
+ const ecConfig = input.ecConfig as StarlightExpressiveCodeOptions;
80
+ const { locales } = starlightConfig;
76
81
 
77
- const {
78
- themes: themesInput,
79
- customizeTheme,
80
- styleOverrides: { textMarkers: textMarkersStyleOverrides, ...otherStyleOverrides } = {},
81
- useStarlightDarkModeSwitch,
82
- useStarlightUiThemeColors = config.themes === undefined,
83
- plugins = [],
84
- ...rest
85
- } = config;
82
+ const {
83
+ themes: themesInput,
84
+ customizeTheme,
85
+ styleOverrides: { textMarkers: textMarkersStyleOverrides, ...otherStyleOverrides } = {},
86
+ useStarlightDarkModeSwitch,
87
+ useStarlightUiThemeColors = ecConfig.themes === undefined,
88
+ plugins = [],
89
+ ...rest
90
+ } = ecConfig;
86
91
 
87
- // Handle the `themes` option
88
- const themes = preprocessThemes(themesInput);
89
- if (useStarlightUiThemeColors === true && themes.length < 2) {
90
- console.warn(
91
- `*** Warning: Using the config option "useStarlightUiThemeColors: true" ` +
92
- `with a single theme is not recommended. For better color contrast, ` +
93
- `please provide at least one dark and one light theme.\n`
94
- );
95
- }
92
+ // Handle the `themes` option
93
+ const themes = preprocessThemes(themesInput);
94
+ if (useStarlightUiThemeColors === true && themes.length < 2) {
95
+ console.warn(
96
+ `*** Warning: Using the config option "useStarlightUiThemeColors: true" ` +
97
+ `with a single theme is not recommended. For better color contrast, ` +
98
+ `please provide at least one dark and one light theme.\n`
99
+ );
100
+ }
96
101
 
97
- // Add the `not-content` class to all rendered blocks to prevent them from being affected
98
- // by Starlight's default content styles
99
- plugins.push({
100
- name: 'Starlight Plugin',
101
- hooks: {
102
- postprocessRenderedBlock: ({ renderData }) => {
103
- addClassName(renderData.blockAst, 'not-content');
102
+ // Add the `not-content` class to all rendered blocks to prevent them from being affected
103
+ // by Starlight's default content styles
104
+ plugins.push({
105
+ name: 'Starlight Plugin',
106
+ hooks: {
107
+ postprocessRenderedBlock: ({ renderData }) => {
108
+ addClassName(renderData.blockAst, 'not-content');
109
+ },
104
110
  },
105
- },
106
- });
111
+ });
107
112
 
108
- // Add Expressive Code UI translations (if any) for all defined locales
109
- addTranslations(locales, useTranslations);
113
+ // Add Expressive Code UI translations (if any) for all defined locales
114
+ if (useTranslations) addTranslations(locales, useTranslations);
110
115
 
111
- return [
112
- astroExpressiveCode({
116
+ return {
113
117
  themes,
114
118
  customizeTheme: (theme) => {
115
119
  if (useStarlightUiThemeColors) {
@@ -151,6 +155,36 @@ export const starlightExpressiveCode = ({
151
155
  getBlockLocale: ({ file }) => pathToLocale(file.path, { starlightConfig, astroConfig }),
152
156
  plugins,
153
157
  ...rest,
158
+ };
159
+ };
160
+ }
161
+
162
+ export const starlightExpressiveCode = ({
163
+ starlightConfig,
164
+ useTranslations,
165
+ }: StarlightEcIntegrationOptions): AstroIntegration[] => {
166
+ if (starlightConfig.expressiveCode === false) return [];
167
+
168
+ const configArgs =
169
+ typeof starlightConfig.expressiveCode === 'object'
170
+ ? (starlightConfig.expressiveCode as AstroExpressiveCodeOptions)
171
+ : {};
172
+ return [
173
+ astroExpressiveCode({
174
+ ...configArgs,
175
+ customConfigPreprocessors: {
176
+ preprocessAstroIntegrationConfig: getStarlightEcConfigPreprocessor({
177
+ starlightConfig,
178
+ useTranslations,
179
+ }),
180
+ preprocessComponentConfig: `
181
+ import starlightConfig from 'virtual:starlight/user-config'
182
+ import { useTranslations } from '@astrojs/starlight/internal'
183
+ import { getStarlightEcConfigPreprocessor } from '@astrojs/starlight/expressive-code'
184
+
185
+ export default getStarlightEcConfigPreprocessor({ starlightConfig, useTranslations })
186
+ `,
187
+ },
154
188
  }),
155
189
  ];
156
190
  };
@@ -1,5 +1,6 @@
1
- import fs from 'node:fs';
2
1
  import { ExpressiveCodeTheme, type ThemeObjectOrShikiThemeName } from 'astro-expressive-code';
2
+ import nightOwlDark from './themes/night-owl-dark.jsonc?raw';
3
+ import nightOwlLight from './themes/night-owl-light.jsonc?raw';
3
4
 
4
5
  export type BundledThemeName = 'starlight-dark' | 'starlight-light';
5
6
 
@@ -20,13 +21,8 @@ export function preprocessThemes(
20
21
  return themes.map((theme) => {
21
22
  // If the current entry is the name of a bundled theme, load it
22
23
  if (theme === 'starlight-dark' || theme === 'starlight-light') {
23
- const bundledThemeFile =
24
- theme === 'starlight-dark' ? 'night-owl-dark.jsonc' : 'night-owl-light.jsonc';
25
- return customizeBundledTheme(
26
- ExpressiveCodeTheme.fromJSONString(
27
- fs.readFileSync(new URL(`./themes/${bundledThemeFile}`, import.meta.url), 'utf-8')
28
- )
29
- );
24
+ const bundledTheme = theme === 'starlight-dark' ? nightOwlDark : nightOwlLight;
25
+ return customizeBundledTheme(ExpressiveCodeTheme.fromJSONString(bundledTheme));
30
26
  }
31
27
  // Otherwise, just pass it through
32
28
  return theme;
@@ -22,11 +22,13 @@ export function pathToLocale(
22
22
  ): string | undefined {
23
23
  const srcDir = new URL(astroConfig.srcDir, astroConfig.root);
24
24
  const docsDir = new URL('content/docs/', srcDir);
25
- const slug = path
26
- // Format path to unix style path.
27
- ?.replace(/\\/g, '/')
28
- // Strip docs path leaving only content collection file ID.
29
- // Example: /Users/houston/repo/src/content/docs/en/guide.md => en/guide.md
30
- .replace(docsDir.pathname, '');
25
+ // Format path to unix style path.
26
+ path = path?.replace(/\\/g, '/');
27
+ // Ensure that the page path starts with a slash if the docs directory also does,
28
+ // which makes stripping the docs path in the next step work on Windows, too.
29
+ if (path && !path.startsWith('/') && docsDir.pathname.startsWith('/')) path = '/' + path;
30
+ // Strip docs path leaving only content collection file ID.
31
+ // Example: /Users/houston/repo/src/content/docs/en/guide.md => en/guide.md
32
+ const slug = path?.replace(docsDir.pathname, '');
31
33
  return slugToLocale(slug, starlightConfig.locales);
32
34
  }
package/internal.ts ADDED
@@ -0,0 +1 @@
1
+ export { useTranslations } from './utils/translations';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/starlight",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "Build beautiful, high-performance documentation websites with Astro",
5
5
  "keywords": [
6
6
  "docs",
@@ -150,6 +150,7 @@
150
150
  "types": "./components/Search.astro.tsx",
151
151
  "import": "./components/Search.astro"
152
152
  },
153
+ "./internal": "./internal.ts",
153
154
  "./props": "./props.ts",
154
155
  "./schema": "./schema.ts",
155
156
  "./types": "./types.ts",
@@ -174,7 +175,7 @@
174
175
  "@pagefind/default-ui": "^1.0.3",
175
176
  "@types/hast": "^3.0.3",
176
177
  "@types/mdast": "^4.0.3",
177
- "astro-expressive-code": "^0.31.0",
178
+ "astro-expressive-code": "^0.32.2",
178
179
  "bcp-47": "^2.1.0",
179
180
  "hast-util-select": "^6.0.2",
180
181
  "hastscript": "^8.0.0",
package/style/props.css CHANGED
@@ -87,8 +87,8 @@
87
87
  'Segoe UI Symbol', 'Noto Color Emoji';
88
88
  --sl-font-system-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
89
89
  'Courier New', monospace;
90
- --__sl-font: var(--sl-font, ''), var(--sl-font-system);
91
- --__sl-font-mono: var(--sl-font-mono, ''), var(--sl-font-system-mono);
90
+ --__sl-font: var(--sl-font, var(--sl-font-system)), var(--sl-font-system);
91
+ --__sl-font-mono: var(--sl-font-mono, var(--sl-font-system-mono)), var(--sl-font-system-mono);
92
92
 
93
93
  /** Key layout values */
94
94
  --sl-nav-height: 3.5rem;
@@ -205,6 +205,9 @@ const UserConfigSchema = z.object({
205
205
  .string()
206
206
  .default('|')
207
207
  .describe('Will be used as title delimiter in the generated `<title>` tag.'),
208
+
209
+ /** Disable Starlight's default 404 page. */
210
+ disable404Route: z.boolean().default(false).describe("Disable Starlight's default 404 page."),
208
211
  });
209
212
 
210
213
  export const StarlightConfigSchema = UserConfigSchema.strict().transform(