diff --git a/.changeset/violet-planes-appear.md b/.changeset/violet-planes-appear.md deleted file mode 100644 index ecc6363f78..0000000000 --- a/.changeset/violet-planes-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@evidence-dev/preprocess': patch ---- - -Eliminate requirement to add blank lines to get markdown formatting diff --git a/packages/lib/preprocess/index.cjs b/packages/lib/preprocess/index.cjs index 4c4cc9aef8..93f9fafcd2 100644 --- a/packages/lib/preprocess/index.cjs +++ b/packages/lib/preprocess/index.cjs @@ -1,9 +1,7 @@ const mdsvex = require('mdsvex'); const { highlighter } = require('./src/utils/highlighter.cjs'); const addScriptTags = require('./src/add-script-tags.cjs'); -const addBlankLines = require('./src/add-blank-lines.cjs'); const processQueries = require('./src/process-queries.cjs'); -const fixListsWithinEachBlocks = require('./src/fix-lists-within-each-blocks.cjs'); const addClasses = require('./src/add-classes.cjs'); // This is includes future proofing to add support for Prism highlighting const processFrontmatter = require('./src/frontmatter/process-frontmatter.cjs'); @@ -16,7 +14,6 @@ module.exports = function evidencePreprocess(componentDevelopmentMode = false) { injectPartials, addScriptTags, processQueries.processQueries(componentDevelopmentMode), - addBlankLines, mdsvex.mdsvex({ extensions: ['.md'], smartypants: { @@ -45,7 +42,6 @@ module.exports = function evidencePreprocess(componentDevelopmentMode = false) { ] ] }), - fixListsWithinEachBlocks, // Add both script tags to all markdown files, if they are missing processFrontmatter() diff --git a/packages/lib/preprocess/src/add-blank-lines.cjs b/packages/lib/preprocess/src/add-blank-lines.cjs deleted file mode 100644 index a6608c649d..0000000000 --- a/packages/lib/preprocess/src/add-blank-lines.cjs +++ /dev/null @@ -1,54 +0,0 @@ -/** - * This preprocessor adds blank lines around certain elements in markdown files to prevent - * mdsvex from wrapping test in paragraph tags and breaking the formatting. - * - * @satisfies {import("svelte-preprocess/dist/types").PreprocessorGroup} - */ -const addBlankLines = { - markup({ content, filename }) { - if (!filename?.endsWith('.md')) return { code: content }; - - // Array to store code blocks and inline code snippets temporarily - /** @type {string[]} */ - const placeholders = []; - let index = 0; - - // Regex for code blocks with varying backtick delimiters (``` or more) - const codeBlockRegex = /(`{3,})[\s\S]*?\1/g; - - // Regex for inline code snippets (`...`) - const inlineCodeRegex = /`[^`]*`/g; - - // Replace code blocks and inline snippets with placeholders - const contentWithoutCode = content - .replace(codeBlockRegex, (match) => { - placeholders.push(match); - return `__CODE_BLOCK_${index++}__`; - }) - .replace(inlineCodeRegex, (match) => { - placeholders.push(match); - return `__INLINE_CODE_${index++}__`; - }); - - // Updated regex to correctly handle multiline attributes and blank lines - const modifiedContent = contentWithoutCode - .replace(/(<[A-Z][\w:-]*\s*(?:".*?"|'.*?'|[^>])*?[^/]>)(\n\s*\S)/g, '$1\n$2') - .replace(/(\{\/if|\{:else(?: if [^}]+)?\})/g, `\n$1`) - .replace(/\{\/each\}/g, `\n{/each}\n`) - .replace(/(<[A-Z][\w:-]*)([^>]*)(\/?>)/g, (_, start, middle, slashEnd) => { - // Remove extra blank lines in the attributes - // e.g. replace multiple newlines or purely blank lines with a single newline - const cleanedMiddle = middle.replace(/\n\s*\n\s*/g, '\n'); - return start + cleanedMiddle + slashEnd; - }); - - // Restore placeholders with their original content - const finalContent = modifiedContent - .replace(/__CODE_BLOCK_(\d+)__/g, (_, i) => placeholders[i]) - .replace(/__INLINE_CODE_(\d+)__/g, (_, i) => placeholders[i]); - - return { code: finalContent }; - } -}; - -module.exports = addBlankLines; diff --git a/packages/lib/preprocess/src/add-blank-lines.spec.cjs b/packages/lib/preprocess/src/add-blank-lines.spec.cjs deleted file mode 100644 index feab97d7ef..0000000000 --- a/packages/lib/preprocess/src/add-blank-lines.spec.cjs +++ /dev/null @@ -1,74 +0,0 @@ -import { describe, it } from 'vitest'; -import addBlankLines from '../src/add-blank-lines.cjs'; -import { expectEqualIgnoringIndentation } from './test-utils'; - -describe('addBlankLines Preprocessor', () => { - it('should add a blank line after a component opening tag', () => { - const input = `
- Here is a metric definition -
`; - const expectedOutput = `
- - Here is a metric definition -
`; - const result = addBlankLines.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should add a newline before and after {/each}', () => { - const input = `{#each items as item} -{item.name} -{/each}`; - const expectedOutput = `{#each items as item} -{item.name} - -{/each} -`; - - const result = addBlankLines.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should add a newline before {:else}', () => { - const input = `{#if condition} -Condition met -{:else} -Condition not met -{/if}`; - const expectedOutput = `{#if condition} -Condition met - -{:else} -Condition not met - -{/if}`; - - const result = addBlankLines.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should clean up excessive blank lines in attributes', () => { - const input = ``; - const expectedOutput = ``; - - const result = addBlankLines.markup({ content: input, filename: 'test.md' }); - - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should handle a component with no attributes', () => { - const input = ``; - const expectedOutput = ``; - - const result = addBlankLines.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); -}); diff --git a/packages/lib/preprocess/src/fix-lists-within-each-blocks.cjs b/packages/lib/preprocess/src/fix-lists-within-each-blocks.cjs deleted file mode 100644 index ac29a6a2e2..0000000000 --- a/packages/lib/preprocess/src/fix-lists-within-each-blocks.cjs +++ /dev/null @@ -1,86 +0,0 @@ -/** - * This preprocessor moves `
    ` and `
      ` elements outside of each blocks. This is necessary because mdsvex will - * add a `
        ` or `
          ` within the each block when users create a list inside an each block. - * - * For example, when the user writes - * {#each items as item} - * - {item} - * {/each} - * - * mdsvex will generate: - * {#each items as item} - *
            - *
          • {item}
          • - *
          - * {/each} - * - * Which will create a new list for every element. - * - * This preprocessor ensures that the generated HTML is: - *
            - * {#each items as item} - *
          • {item}
          • - * {/each} - *
          - * - * @satisfies {import("svelte-preprocess/dist/types").PreprocessorGroup} - */ -const fixListsWithinEachBlocks = { - markup({ content, filename }) { - if (!filename?.endsWith('.md')) return { code: content }; - - // Array to store code blocks and inline code snippets temporarily - /** @type {string[]} */ const placeholders = []; - let index = 0; - - const codeBlockRegex = /(`{3,})[\s\S]*?\1/g; - const inlineCodeRegex = /`[^`]*`/g; - - const contentWithoutCode = content - .replace(codeBlockRegex, (match) => { - placeholders.push(match); - return `__CODE_BLOCK_${index++}__`; - }) - .replace(inlineCodeRegex, (match) => { - placeholders.push(match); - return `__INLINE_CODE_${index++}__`; - }); - - // Scenario 1 - Text without blank line before closing each tag - const scenario1Regex = /\{#each([^}]*)\}([\s\S]*?)\{\/each\}<\/p>/gm; - const scenario1Replacement = `{#each$1}$2

          \n{/each}`; - - // Scenario 2 - Lists without blank line before closing each tag - const scenario2Regex = - /\{#each([^}]*)\}\s*<(u|o)l([^>]*)>\s*]*)>([\s\S]*?)\{\/each\}\s*<\/li>\s*<\/\2l>/gm; - const scenario2Replacement = `<$2l$3> - {#each$1} - $5 - - {/each} - `; - - // Scenario 3 - List with blank line before closing each tag - const scenario3Regex = - /\{#each([^}]*)\}\s*<(u|o)l([^>]*)>\s*]*)>([\s\S]*?)<\/li>\s*<\/\2l>\s*\{\/each\}/gm; - const scenario3Replacement = `<$2l$3> - {#each$1}$5 - - {/each} - `; - - let modifiedContent = contentWithoutCode; - modifiedContent = modifiedContent.replace(scenario1Regex, scenario1Replacement); - modifiedContent = modifiedContent.replace(scenario2Regex, scenario2Replacement); - modifiedContent = modifiedContent.replace(scenario3Regex, scenario3Replacement); - - // Restore the placeholders - const finalContent = modifiedContent - .replace(/__CODE_BLOCK_(\d+)__/g, (_, i) => placeholders[i]) - .replace(/__INLINE_CODE_(\d+)__/g, (_, i) => placeholders[i]); - - return { code: finalContent }; - } -}; - -module.exports = fixListsWithinEachBlocks; diff --git a/packages/lib/preprocess/src/fix-lists-within-each-blocks.spec.cjs b/packages/lib/preprocess/src/fix-lists-within-each-blocks.spec.cjs deleted file mode 100644 index 8395d3933c..0000000000 --- a/packages/lib/preprocess/src/fix-lists-within-each-blocks.spec.cjs +++ /dev/null @@ -1,114 +0,0 @@ -import { describe, it } from 'vitest'; -import fixListsWithinEachBlocks from './fix-lists-within-each-blocks.cjs'; -import { expectEqualIgnoringIndentation } from './test-utils.js'; - -describe('fixListsWithinEachBlocks Preprocessor', () => { - it('should handle text without blank line before closing each tag', () => { - const input = `{#each something as thing} -

          text -{/each}

          `; - const expectedOutput = `{#each something as thing} -

          text -

          -{/each}`; - - const result = fixListsWithinEachBlocks.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should handle unordered lists without blank line before closing each tag', () => { - const input = ` -{#each something as thing}
            -
          • one -{/each}
          `; - const expectedOutput = ` -
            -{#each something as thing} -
          • one -
          • -{/each} -
          `; - - const result = fixListsWithinEachBlocks.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should handle ordered lists without blank line before closing each tag', () => { - const input = ` -{#each something as thing}
            -
          1. one -{/each}
          `; - const expectedOutput = ` -
            -{#each something as thing} -
          1. one -
          2. -{/each} -
          `; - - const result = fixListsWithinEachBlocks.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should handle unordered lists contained inside each tags', () => { - const input = `{#each something as thing}
            -
          • one
          • -
          -{/each}`; - const expectedOutput = `
            -{#each something as thing}
          • one -
          • -{/each} -
          `; - - const result = fixListsWithinEachBlocks.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should handle ordered lists contained inside each tags', () => { - const input = `{#each something as thing}
            -
          1. one
          2. -
          -{/each}`; - const expectedOutput = `
            -{#each something as thing}
          1. one -
          2. -{/each} -
          `; - - const result = fixListsWithinEachBlocks.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); - - it('should preserve code blocks and inline code snippets', () => { - const input = `# Markdown with Code - -\`\`\`js -console.log('Code block'); -\`\`\` - -Inline code: \`console.log('inline');\` - -{#each items as item} -
            -
          • {item.name}
          • -
          -{/each}`; - const expectedOutput = `# Markdown with Code - -\`\`\`js -console.log('Code block'); -\`\`\` - -Inline code: \`console.log('inline');\` - -
            - {#each items as item}
          • {item.name} -
          • - {/each} -
          `; - - const result = fixListsWithinEachBlocks.markup({ content: input, filename: 'test.md' }); - expectEqualIgnoringIndentation(result.code, expectedOutput); - }); -}); diff --git a/packages/lib/preprocess/src/test-utils.js b/packages/lib/preprocess/src/test-utils.js deleted file mode 100644 index 1c86124dbe..0000000000 --- a/packages/lib/preprocess/src/test-utils.js +++ /dev/null @@ -1,40 +0,0 @@ -import { expect } from 'vitest'; - -/** - * Removes whitespace from the start of each line - * @example - * ``` - * no whitespace - * spaces - * tabs - * ``` - * becomes - * ``` - * no whitespace - * spaces - * tabs - * ``` - * @param {string} s - * @returns {string} s with whitespace at the beginning of each line removed - */ -export const removeIndentation = (s) => s.replace(/^\s+/gm, ''); - -/** - * Expects a and b to be equal, ignoring indentation - * @example - * ```js - * expectEqualIgnoringIndentation( - * ` a\n\tb`, - * `a\nb`, - * ) => true - * - * expectEqualIgnoringIndentation( - * `a\n\nb`, - * `a\nb` - * ) => false - * ``` - * @param {string} a - * @param {string} b - */ -export const expectEqualIgnoringIndentation = (a, b) => - expect(removeIndentation(a)).toBe(removeIndentation(b));