From 9fe847544f1edb85bf5b25cd81db39227814335e Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Wed, 8 May 2024 00:22:07 +0200 Subject: Add more diagnostic help to error messages thrown by `` (#1838) --- .changeset/breezy-cats-taste.md | 5 ++ .../__tests__/remark-rehype/rehype-steps.test.ts | 28 ++++++++++-- packages/starlight/package.json | 1 + packages/starlight/user-components/rehype-steps.ts | 26 +++++++---- pnpm-lock.yaml | 53 ++++++++++++++++++++++ 5 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 .changeset/breezy-cats-taste.md diff --git a/.changeset/breezy-cats-taste.md b/.changeset/breezy-cats-taste.md new file mode 100644 index 00000000..3f2add68 --- /dev/null +++ b/.changeset/breezy-cats-taste.md @@ -0,0 +1,5 @@ +--- +'@astrojs/starlight': patch +--- + +Adds extra information to the errors thrown by the `` component to help locate misformatted code diff --git a/packages/starlight/__tests__/remark-rehype/rehype-steps.test.ts b/packages/starlight/__tests__/remark-rehype/rehype-steps.test.ts index d97f7db8..36f4082c 100644 --- a/packages/starlight/__tests__/remark-rehype/rehype-steps.test.ts +++ b/packages/starlight/__tests__/remark-rehype/rehype-steps.test.ts @@ -29,16 +29,36 @@ test('component with non-`
    ` content throws an error', () => { "[AstroUserError]: The \`\` component expects its content to be a single ordered list (\`
      \`) but found the following element: \`

      \`. Hint: - To learn more about the \`\` component, see https://starlight.astro.build/guides/components/#steps" + To learn more about the \`\` component, see https://starlight.astro.build/guides/components/#steps + + Full HTML passed to \`\`: + +

      A paragraph is not an ordered list

      + " `); }); test('component with multiple children throws an error', () => { - expect(() => processSteps('
          ')).toThrowErrorMatchingInlineSnapshot(` + expect(() => + processSteps( + '
          1. List item

          I intended this to be part of the same list item

          1. Other list item
          ' + ) + ).toThrowErrorMatchingInlineSnapshot(` "[AstroUserError]: - The \`\` component expects its content to be a single ordered list (\`
            \`) but found multiple child elements: \`
              \`, \`
                \`. + The \`\` component expects its content to be a single ordered list (\`
                  \`) but found multiple child elements: \`
                    \`, \`

                    \`, \`

                      \`. Hint: - To learn more about the \`\` component, see https://starlight.astro.build/guides/components/#steps" + To learn more about the \`\` component, see https://starlight.astro.build/guides/components/#steps + + Full HTML passed to \`\`: + +
                        +
                      1. List item
                      2. +
                      +

                      I intended this to be part of the same list item

                      +
                        +
                      1. Other list item
                      2. +
                      + " `); }); diff --git a/packages/starlight/package.json b/packages/starlight/package.json index 70ff9ed4..2e503cf8 100644 --- a/packages/starlight/package.json +++ b/packages/starlight/package.json @@ -206,6 +206,7 @@ "mdast-util-to-markdown": "^2.1.0", "pagefind": "^1.0.3", "rehype": "^13.0.1", + "rehype-format": "^5.0.0", "remark-directive": "^3.0.0", "unified": "^11.0.4", "unist-util-visit": "^5.0.0", diff --git a/packages/starlight/user-components/rehype-steps.ts b/packages/starlight/user-components/rehype-steps.ts index dd912ced..ff9c9593 100644 --- a/packages/starlight/user-components/rehype-steps.ts +++ b/packages/starlight/user-components/rehype-steps.ts @@ -1,11 +1,17 @@ import { AstroError } from 'astro/errors'; import type { Element, Root } from 'hast'; import { rehype } from 'rehype'; +import rehypeFormat from 'rehype-format'; +import type { VFile } from 'vfile'; + +const prettyPrintProcessor = rehype().data('settings', { fragment: true }).use(rehypeFormat); +const prettyPrintHtml = (html: string) => + prettyPrintProcessor.processSync({ value: html }).toString(); const stepsProcessor = rehype() .data('settings', { fragment: true }) .use(function steps() { - return (tree: Root) => { + return (tree: Root, vfile: VFile) => { const rootElements = tree.children.filter((item): item is Element => item.type === 'element'); const [rootElement] = rootElements; @@ -17,12 +23,14 @@ const stepsProcessor = rehype() throw new StepsError( 'The `` component expects its content to be a single ordered list (`
                        `) but found multiple child elements: ' + rootElements.map((element: Element) => `\`<${element.tagName}>\``).join(', ') + - '.' + '.', + vfile.value.toString() ); } else if (rootElement.tagName !== 'ol') { throw new StepsError( 'The `` component expects its content to be a single ordered list (`
                          `) but found the following element: ' + - `\`<${rootElement.tagName}>\`.` + `\`<${rootElement.tagName}>\`.`, + vfile.value.toString() ); } @@ -49,10 +57,12 @@ export const processSteps = (html: string | undefined) => { }; class StepsError extends AstroError { - constructor(message: string) { - super( - message, - 'To learn more about the `` component, see https://starlight.astro.build/guides/components/#steps' - ); + constructor(message: string, html?: string) { + let hint = + 'To learn more about the `` component, see https://starlight.astro.build/guides/components/#steps'; + if (html) { + hint += '\n\nFull HTML passed to ``:\n' + prettyPrintHtml(html); + } + super(message, hint); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b2c15cfc..97c6b646 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -175,6 +175,9 @@ importers: rehype: specifier: ^13.0.1 version: 13.0.1 + rehype-format: + specifier: ^5.0.0 + version: 5.0.0 remark-directive: specifier: ^3.0.0 version: 3.0.0 @@ -3461,6 +3464,13 @@ packages: dependencies: function-bind: 1.1.2 + /hast-util-embedded@3.0.0: + resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + dependencies: + '@types/hast': 3.0.3 + hast-util-is-element: 3.0.0 + dev: false + /hast-util-from-html@2.0.1: resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==} dependencies: @@ -3488,6 +3498,12 @@ packages: dependencies: '@types/hast': 3.0.3 + /hast-util-is-body-ok-link@3.0.0: + resolution: {integrity: sha512-VFHY5bo2nY8HiV6nir2ynmEB1XkxzuUffhEGeVx7orbu/B1KaGyeGgMZldvMVx5xWrDlLLG/kQ6YkJAMkBEx0w==} + dependencies: + '@types/hast': 3.0.3 + dev: false + /hast-util-is-element@3.0.0: resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} dependencies: @@ -3499,6 +3515,16 @@ packages: dependencies: '@types/hast': 3.0.3 + /hast-util-phrasing@3.0.1: + resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} + dependencies: + '@types/hast': 3.0.3 + hast-util-embedded: 3.0.0 + hast-util-has-property: 3.0.0 + hast-util-is-body-ok-link: 3.0.0 + hast-util-is-element: 3.0.0 + dev: false + /hast-util-raw@9.0.1: resolution: {integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==} dependencies: @@ -3692,6 +3718,10 @@ packages: /html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + /html-whitespace-sensitive-tag-names@3.0.0: + resolution: {integrity: sha512-KlClZ3/Qy5UgvpvVvDomGhnQhNWH5INE8GwvSIQ9CWt1K0zbbXrl7eN5bWaafOZgtmO3jMPwUqmrmEwinhPq1w==} + dev: false + /html_codesniffer@2.5.1: resolution: {integrity: sha512-vcz0yAaX/OaV6sdNHuT9alBOKkSxYb8h5Yq26dUqgi7XmCgGUSa7U9PiY1PBXQFMjKv1wVPs5/QzHlGuxPDUGg==} engines: {node: '>=6'} @@ -5711,6 +5741,29 @@ packages: expressive-code: 0.35.2 dev: false + /rehype-format@5.0.0: + resolution: {integrity: sha512-kM4II8krCHmUhxrlvzFSptvaWh280Fr7UGNJU5DCMuvmAwGCNmGfi9CvFAQK6JDjsNoRMWQStglK3zKJH685Wg==} + dependencies: + '@types/hast': 3.0.3 + hast-util-embedded: 3.0.0 + hast-util-is-element: 3.0.0 + hast-util-phrasing: 3.0.1 + hast-util-whitespace: 3.0.0 + html-whitespace-sensitive-tag-names: 3.0.0 + rehype-minify-whitespace: 6.0.0 + unist-util-visit-parents: 6.0.1 + dev: false + + /rehype-minify-whitespace@6.0.0: + resolution: {integrity: sha512-i9It4YHR0Sf3GsnlR5jFUKXRr9oayvEk9GKQUkwZv6hs70OH9q3OCZrq9PpLvIGKt3W+JxBOxCidNVpH/6rWdA==} + dependencies: + '@types/hast': 3.0.3 + hast-util-embedded: 3.0.0 + hast-util-is-element: 3.0.0 + hast-util-whitespace: 3.0.0 + unist-util-is: 6.0.0 + dev: false + /rehype-parse@9.0.0: resolution: {integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==} dependencies: -- cgit