summaryrefslogtreecommitdiff
path: root/src/compiler/transform/prism.ts
blob: 1bb024a8455cdefc2d649828672735dde47ee2f3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import type { Transformer } from '../../@types/transformer';
import type { Script } from '../../parser/interfaces';
import { getAttrValue } from '../../ast.js';

const PRISM_IMPORT = `import Prism from 'astro/components/Prism.astro';\n`;
const prismImportExp = /import Prism from ['"]astro\/components\/Prism.astro['"]/;
/** escaping code samples that contain template string replacement parts, ${foo} or example. */
function escape(code: string) {
  return code.replace(/[`$]/g, (match) => {
    return '\\' + match;
  });
}
/** default export - Transform prism   */
export default function (module: Script): Transformer {
  let usesPrism = false;

  return {
    visitors: {
      html: {
        Element: {
          enter(node) {
            if (node.name !== 'code') return;
            const className = getAttrValue(node.attributes, 'class') || '';
            const classes = className.split(' ');

            let lang;
            for (let cn of classes) {
              const matches = /language-(.+)/.exec(cn);
              if (matches) {
                lang = matches[1];
              }
            }

            if (!lang) return;

            let code;
            if (node.children?.length) {
              code = node.children[0].data;
            }

            const repl = {
              start: 0,
              end: 0,
              type: 'InlineComponent',
              name: 'Prism',
              attributes: [
                {
                  type: 'Attribute',
                  name: 'lang',
                  value: [
                    {
                      type: 'Text',
                      raw: lang,
                      data: lang,
                    },
                  ],
                },
                {
                  type: 'Attribute',
                  name: 'code',
                  value: [
                    {
                      type: 'MustacheTag',
                      expression: {
                        type: 'Expression',
                        codeChunks: ['`' + escape(code) + '`'],
                        children: [],
                      },
                    },
                  ],
                },
              ],
              children: [],
            };

            this.replace(repl);
            usesPrism = true;
          },
        },
      },
    },
    async finalize() {
      // Add the Prism import if needed.
      if (usesPrism && !prismImportExp.test(module.content)) {
        module.content = PRISM_IMPORT + module.content;
      }
    },
  };
}