import { test } from 'uvu' import * as assert from 'uvu/assert' import { visit } from 'unist-util-visit' import { rehype } from 'rehype' import { unified } from 'unified' import remarkParse from 'remark-parse' import remarkRehype from 'remark-rehype' import rehypeStringify from 'rehype-stringify' import dedent from 'dedent' import rehypePrism from './src/index.js' /** * Mock meta in code block */ const addMeta = (metastring) => { if (!metastring) return return (tree) => { visit(tree, 'element', (node, index, parent) => { if (node.tagName === 'code') { node.data = { meta: metastring } } }) } } const processHtml = (html, options, metastring) => { return rehype() .data('settings', { fragment: true }) .use(addMeta, metastring) .use(rehypePrism, options) .processSync(html) .toString() } const processHtmlUnified = (html, options, metastring) => { return unified() .use(remarkParse) .use(remarkRehype, {}) .use(addMeta, metastring) .use(rehypePrism, options) .use(rehypeStringify) .processSync(html) .toString() } test('adds a code-highlight class to the code and pre tag', () => { const result = processHtml(dedent`
`) const expected = dedent`
` assert.is(result, expected) }) test('add span with class code line for each line', () => { const result = processHtml( dedent`
x = 6
` ) const expected = dedent`
x = 6
` assert.is(result, expected) }) test('finds code and highlights', () => { const result = processHtml(dedent`

foo

x = 6
`).trim() const expected = dedent`

foo

x = 6
` assert.is(result, expected) }) test('respects line spacing', () => { const result = processHtml(dedent`
x

y
`).trim() const expected = dedent`
x
  
  y
  
` assert.is(result, expected) }) test('handles uppercase correctly', () => { const result = processHtml(dedent`

foo

x = 6
`).trim() const expected = dedent`

foo

x = 6
` assert.is(result, expected) }) test('each line of code should be a separate div', async () => { const result = processHtml(dedent`

foo

      x = 6
      y = 7
      
      
`).trim() const codeLineCount = (result.match(//g) || []).length assert.is(codeLineCount, 2) }) test('should highlight line', async () => { const meta = '{1}' const result = processHtml( dedent`
      x = 6
      y = 7
      
      
`, {}, meta ).trim() const codeHighlightCount = (result.match(//g) || []).length assert.is(codeHighlightCount, 1) }) test('should highlight comma separated lines', async () => { const meta = '{1,3}' const result = processHtml( dedent`
      x = 6
      y = 7
      z = 10
      
      
`, {}, meta ).trim() const codeHighlightCount = (result.match(//g) || []).length assert.is(codeHighlightCount, 2) }) test('should should parse ranges with a space in between', async () => { const meta = '{1, 3}' const result = processHtml( dedent`
      x = 6
      y = 7
      z = 10
      
      
`, {}, meta ).trim() const codeHighlightCount = (result.match(//g) || []).length assert.is(codeHighlightCount, 2) }) test('should highlight range separated lines', async () => { const meta = '{1-3}' const result = processHtml( dedent`
      x = 6
      y = 7
      z = 10
      
      
`, {}, meta ).trim() const codeHighlightCount = (result.match(//g) || []).length assert.is(codeHighlightCount, 3) }) test('showLineNumbers option add line numbers', async () => { const result = processHtml( dedent`
      x = 6
      y = 7
      
      
`, { showLineNumbers: true } ).trim() assert.ok(result.match(/line="1"/g)) assert.ok(result.match(/line="2"/g)) assert.not(result.match(/line="3"/g)) }) test('not show line number when showLineNumbers=false', async () => { const meta = 'showLineNumbers=false' const result = processHtml( dedent`
      x = 6
      y = 7
      
      
`, { showLineNumbers: true }, meta ).trim() assert.not(result.match(/line="1"/g)) assert.not(result.match(/line="2"/g)) }) test('not show line number when showLineNumbers={false}', async () => { const meta = 'showLineNumbers={false}' const result = processHtml( dedent`
      x = 6
      y = 7
      
      
`, { showLineNumbers: true }, meta ).trim() assert.not(result.match(/line="1"/g)) assert.not(result.match(/line="2"/g)) }) test('showLineNumbers property works in meta field', async () => { const meta = 'showLineNumbers' const result = processHtml( dedent`
      x = 6
      y = 7
      
      
`, {}, meta ).trim() assert.ok(result.match(/line="1"/g)) assert.ok(result.match(/line="2"/g)) assert.not(result.match(/line="3"/g)) }) test('showLineNumbers property with custom index works in meta field', async () => { const meta = 'showLineNumbers=5' const result = processHtml( dedent`
      x = 6
      y = 7
      
      
`, {}, meta ).trim() assert.ok(result.match(/line="5"/g)) assert.ok(result.match(/line="6"/g)) assert.not(result.match(/line="7"/g)) }) test('should support both highlighting and add line number', async () => { const meta = '{1} showLineNumbers' const result = processHtml( dedent`
      x = 6
      y = 7
      z = 10
      
      
`, {}, meta ).trim() const codeHighlightCount = (result.match(/highlight-line/g) || []).length assert.is(codeHighlightCount, 1) assert.ok(result.match(/line="1"/g)) assert.ok(result.match(/line="2"/g)) }) test('throw error with fake language- class', () => { assert.throws( () => processHtml(dedent`
x = 6
`), /Unknown language/ ) }) test('with options.ignoreMissing, does nothing to code block with fake language- class', () => { const result = processHtml( dedent`
x = 6
`, { ignoreMissing: true } ) const expected = dedent`
x = 6
` assert.is(result, expected) }) test('with options.defaultLanguage, it adds the correct language class tag', () => { const result = processHtml( dedent`
x = 6
`, { defaultLanguage: 'py' } ) const expected = dedent`
x = 6
` assert.is(result, expected) }) test('defaultLanguage should produce the same syntax tree as if manually specified', () => { const resultDefaultLanguage = processHtml( dedent`
x = 6
`, { defaultLanguage: 'py' } ) const resultManuallySpecified = processHtml( dedent`
x = 6
` ) assert.is(resultDefaultLanguage, resultManuallySpecified) }) test('throws error if options.defaultLanguage is not registered with refractor', () => { assert.throws( () => processHtml( dedent`
x = 6
`, { defaultLanguage: 'pyzqt' } ), /"pyzqt" is not registered with refractor/ ) }) test('should work with multiline code / comments', () => { const result = processHtml( dedent`

    /**
     * My comment
     */
    
`, { ignoreMissing: true } ) const expected = dedent`

        /**
         * My comment
         */
        
` assert.is(result, expected) }) test('adds inserted or deleted to code-line if lang=diff', async () => { const result = processHtml( dedent`
      + x = 6
- y = 7
z = 10
      
      
` ).trim() assert.ok(result.includes(``)) assert.ok(result.includes(``)) assert.ok(result.includes(``)) }) test('works as a remarkjs / unifiedjs plugin', () => { const result = processHtmlUnified( dedent` ~~~jsx ~~~ `, { ignoreMissing: true } ) const expected = dedent`
<Component/>
  
` assert.is(result, expected) }) test('diff and code highlighting should work together', () => { const result = processHtml( dedent`

    .hello{
    - background:url('./urel.png');
    + background-image:url('./urel.png');
    }
    
`, { ignoreMissing: true } ) assert.ok(result.includes(`
`))
  assert.ok(result.includes(``))
  assert.ok(result.includes(``))
  assert.ok(result.includes(``))
})

test.run()