@scalar/code-highlight 0.3.2 → 0.3.4

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,17 @@
1
1
  # @scalar/code-highlight
2
2
 
3
+ ## 0.3.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#8964](https://github.com/scalar/scalar/pull/8964): fix markdown inside HTML text tags like `<p>` so inline formatting is rendered.
8
+
9
+ ## 0.3.3
10
+
11
+ ### Patch Changes
12
+
13
+ - [#8735](https://github.com/scalar/scalar/pull/8735): fix: replace direct cjs mime and curl dependencies with local implementations
14
+
3
15
  ## 0.3.2
4
16
 
5
17
  ### Patch Changes
@@ -0,0 +1,7 @@
1
+ import type { LanguageFn } from 'highlight.js';
2
+ /**
3
+ * cURL grammar adapted from highlightjs-curl to avoid a CJS dependency.
4
+ */
5
+ declare const curlLanguage: LanguageFn;
6
+ export default curlLanguage;
7
+ //# sourceMappingURL=curl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../src/languages/curl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C;;GAEG;AACH,QAAA,MAAM,YAAY,EAAE,UAyFnB,CAAA;AAED,eAAe,YAAY,CAAA"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * cURL grammar adapted from highlightjs-curl to avoid a CJS dependency.
3
+ */
4
+ const curlLanguage = (hljs) => {
5
+ const quoteString = {
6
+ className: 'string',
7
+ begin: /"/,
8
+ end: /"/,
9
+ contains: [
10
+ hljs.BACKSLASH_ESCAPE,
11
+ {
12
+ className: 'variable',
13
+ begin: /\$\(/,
14
+ end: /\)/,
15
+ contains: [hljs.BACKSLASH_ESCAPE],
16
+ },
17
+ ],
18
+ relevance: 0,
19
+ };
20
+ const optionRequest = {
21
+ className: 'literal',
22
+ begin: /(--request|-X)\s/,
23
+ contains: [
24
+ {
25
+ className: 'symbol',
26
+ begin: /(get|post|delete|options|head|put|patch|trace|connect)/,
27
+ end: /\s/,
28
+ returnEnd: true,
29
+ },
30
+ ],
31
+ returnEnd: true,
32
+ relevance: 10,
33
+ };
34
+ const option = {
35
+ className: 'literal',
36
+ begin: /--/,
37
+ end: /[\s"]/,
38
+ returnEnd: true,
39
+ relevance: 0,
40
+ };
41
+ const optionSingle = {
42
+ className: 'literal',
43
+ begin: /-\w/,
44
+ end: /[\s"]/,
45
+ returnEnd: true,
46
+ relevance: 0,
47
+ };
48
+ const escapedQuote = {
49
+ className: 'string',
50
+ begin: /\\"/,
51
+ relevance: 0,
52
+ };
53
+ const aposString = {
54
+ className: 'string',
55
+ begin: /'/,
56
+ end: /'/,
57
+ relevance: 0,
58
+ };
59
+ const number = {
60
+ className: 'number',
61
+ variants: [{ begin: hljs.C_NUMBER_RE }],
62
+ relevance: 0,
63
+ };
64
+ const pathMode = {
65
+ match: /(\/[a-z._-]+)+/,
66
+ };
67
+ return {
68
+ name: 'curl',
69
+ aliases: ['curl'],
70
+ keywords: 'curl',
71
+ case_insensitive: true,
72
+ contains: [
73
+ optionRequest,
74
+ option,
75
+ optionSingle,
76
+ quoteString,
77
+ escapedQuote,
78
+ aposString,
79
+ hljs.APOS_STRING_MODE,
80
+ hljs.QUOTE_STRING_MODE,
81
+ number,
82
+ pathMode,
83
+ ],
84
+ };
85
+ };
86
+ export default curlLanguage;
@@ -24,7 +24,7 @@ export declare const standardLanguages: {
24
24
  readonly cpp: LanguageFn;
25
25
  readonly csharp: LanguageFn;
26
26
  readonly css: LanguageFn;
27
- readonly curl: any;
27
+ readonly curl: LanguageFn;
28
28
  readonly dart: LanguageFn;
29
29
  readonly diff: LanguageFn;
30
30
  readonly docker: LanguageFn;
@@ -1 +1 @@
1
- {"version":3,"file":"standard.d.ts","sourceRoot":"","sources":["../../src/languages/standard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAiD9C;;;;;;;;;;;;;;GAcG;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDiB,CAAA"}
1
+ {"version":3,"file":"standard.d.ts","sourceRoot":"","sources":["../../src/languages/standard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAgD9C;;;;;;;;;;;;;;GAcG;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDiB,CAAA"}
@@ -43,8 +43,7 @@ import swift from 'highlight.js/lib/languages/swift';
43
43
  import typescript from 'highlight.js/lib/languages/typescript';
44
44
  import xml from 'highlight.js/lib/languages/xml';
45
45
  import yaml from 'highlight.js/lib/languages/yaml';
46
- // @ts-expect-error No types available
47
- import curl from 'highlightjs-curl';
46
+ import curl from './curl.js';
48
47
  /**
49
48
  * We group languages into three categories based on their popularity and usage.
50
49
  * This helps in optimizing the bundle size by allowing users to include only
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/markdown/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAqB,MAAM,OAAO,CAAA;AAsB9E,YAAY,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,IAAI,KAAG,IAAI,IAAI,OAE9C,CAAA;AAqBD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAChC,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,UAuDF;AASD;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAU,GAChB;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,EAAE,CAiBF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,GAAG,MAAM,CAUpE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,YAgC5C"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/markdown/markdown.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAsD,IAAI,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AAuB/G,YAAY,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,IAAI,KAAG,IAAI,IAAI,OAE9C,CAAA;AAiGD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAChC,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,UAyDF;AASD;;GAEG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAU,GAChB;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,EAAE,CAiBF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,GAAG,MAAM,CAUpE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,YAgC5C"}
@@ -1,5 +1,6 @@
1
1
  import rehypeExternalLinks from 'rehype-external-links';
2
2
  import rehypeFormat from 'rehype-format';
3
+ import rehypeParse from 'rehype-parse';
3
4
  import rehypeRaw from 'rehype-raw';
4
5
  import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
5
6
  import rehypeStringify from 'rehype-stringify';
@@ -31,6 +32,69 @@ const transformNodes = (options, ..._ignored) => (tree) => {
31
32
  });
32
33
  return;
33
34
  };
35
+ const TAGS_WITH_INLINE_MARKDOWN = new Set(['dd', 'dt', 'li', 'p', 'summary', 'td', 'th']);
36
+ const MAY_CONTAIN_INLINE_MARKDOWN = /[`*_\[~]/;
37
+ /**
38
+ * Preserve HTML-like text in inline markdown by turning mdast `html` nodes into text nodes.
39
+ */
40
+ const preserveHtmlLikeText = () => (tree) => {
41
+ visit(tree, 'html', (node, index, parent) => {
42
+ if (typeof index !== 'number' || !parent || !('children' in parent) || !Array.isArray(parent.children)) {
43
+ return;
44
+ }
45
+ parent.children[index] = {
46
+ type: 'text',
47
+ value: node.value ?? '',
48
+ };
49
+ });
50
+ };
51
+ const inlineMarkdownProcessor = unified().use(remarkParse).use(remarkGfm).use(preserveHtmlLikeText).use(remarkRehype);
52
+ const htmlFragmentParser = unified().use(rehypeParse, { fragment: true });
53
+ const htmlFragmentStringifier = unified().use(rehypeStringify);
54
+ /**
55
+ * Parse inline markdown and return children from the generated paragraph.
56
+ */
57
+ const extractInlineChildrenFromMarkdown = (value) => {
58
+ const tree = inlineMarkdownProcessor.runSync(inlineMarkdownProcessor.parse(value));
59
+ if (tree.children.length !== 1) {
60
+ return [];
61
+ }
62
+ const paragraph = tree.children.at(0);
63
+ if (!paragraph || paragraph.type !== 'element' || paragraph.tagName !== 'p') {
64
+ return [];
65
+ }
66
+ return paragraph.children;
67
+ };
68
+ /**
69
+ * Re-parses text nodes in selected HTML tags so inline markdown works in tags like <p>.
70
+ */
71
+ const transformInlineMarkdownInHtml = () => (tree) => {
72
+ visit(tree, 'element', (node) => {
73
+ if (!TAGS_WITH_INLINE_MARKDOWN.has(node.tagName)) {
74
+ return;
75
+ }
76
+ node.children = node.children.flatMap((child) => {
77
+ if (child.type !== 'text' || !MAY_CONTAIN_INLINE_MARKDOWN.test(child.value)) {
78
+ return [child];
79
+ }
80
+ const markdownChildren = extractInlineChildrenFromMarkdown(child.value);
81
+ return markdownChildren.length ? markdownChildren : [child];
82
+ });
83
+ });
84
+ };
85
+ /**
86
+ * Rewrites raw HTML strings so inline markdown parsing is only applied to raw HTML input.
87
+ */
88
+ const transformInlineMarkdownInRawHtml = () => (tree) => {
89
+ visit(tree, 'raw', (node) => {
90
+ if (typeof node.value !== 'string' || !MAY_CONTAIN_INLINE_MARKDOWN.test(node.value)) {
91
+ return;
92
+ }
93
+ const htmlFragmentTree = htmlFragmentParser.parse(node.value);
94
+ transformInlineMarkdownInHtml()(htmlFragmentTree);
95
+ node.value = htmlFragmentStringifier.stringify(htmlFragmentTree);
96
+ });
97
+ };
34
98
  /**
35
99
  * Take a Markdown string and generate HTML from it
36
100
  */
@@ -51,7 +115,9 @@ export function htmlFromMarkdown(markdown, options) {
51
115
  .use(remarkRehype, { allowDangerousHtml: true })
52
116
  // Adds GitHub alerts
53
117
  .use(rehypeAlert)
54
- // Creates a HTML AST
118
+ // Parse inline markdown only inside raw HTML fragments, not normal markdown output
119
+ .use(transformInlineMarkdownInRawHtml)
120
+ // Creates an HTML AST
55
121
  .use(rehypeRaw)
56
122
  // Removes disallowed tags
57
123
  .use(rehypeSanitize, {
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "lowlight",
17
17
  "highlightjs"
18
18
  ],
19
- "version": "0.3.2",
19
+ "version": "0.3.4",
20
20
  "engines": {
21
21
  "node": ">=22"
22
22
  },
@@ -73,7 +73,6 @@
73
73
  "dependencies": {
74
74
  "hast-util-to-text": "^4.0.2",
75
75
  "highlight.js": "^11.11.1",
76
- "highlightjs-curl": "^1.3.0",
77
76
  "lowlight": "^3.3.0",
78
77
  "rehype-external-links": "^3.0.0",
79
78
  "rehype-format": "^5.0.1",
@@ -93,12 +92,12 @@
93
92
  "@types/mdast": "^4.0.4",
94
93
  "vfile": "^6.0.3",
95
94
  "vite": "8.0.0",
96
- "@scalar/themes": "0.15.2"
95
+ "@scalar/themes": "0.15.3"
97
96
  },
98
97
  "scripts": {
99
- "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && cp -r src/css dist/css",
98
+ "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && shx cp -r src/css dist/css",
100
99
  "dev": "vite",
101
- "test": "vitest",
100
+ "test": "vitest --run",
102
101
  "types:check": "tsc --noEmit"
103
102
  }
104
103
  }