@brillout/docpress 0.15.10 → 0.15.11-commit-e615832

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.
Files changed (48) hide show
  1. package/Layout.tsx +3 -0
  2. package/autoScrollNav.ts +3 -3
  3. package/code-blocks/components/CodeSnippets.css +74 -0
  4. package/code-blocks/components/CodeSnippets.tsx +51 -0
  5. package/code-blocks/components/Pre.css +51 -0
  6. package/code-blocks/components/Pre.tsx +70 -0
  7. package/code-blocks/hooks/useMDXComponents.tsx +13 -0
  8. package/code-blocks/hooks/useSelectCodeLang.ts +74 -0
  9. package/code-blocks/rehypeMetaToProps.ts +69 -0
  10. package/code-blocks/remarkDetype.ts +192 -0
  11. package/code-blocks/shikiTransformerAutoLinks.ts +61 -0
  12. package/css/button.css +23 -0
  13. package/css/code.css +3 -21
  14. package/css/tooltip.css +10 -2
  15. package/dist/+config.js +1 -1
  16. package/dist/NavItemComponent.js +38 -46
  17. package/dist/code-blocks/rehypeMetaToProps.d.ts +35 -0
  18. package/dist/code-blocks/rehypeMetaToProps.js +62 -0
  19. package/dist/code-blocks/remarkDetype.d.ts +4 -0
  20. package/dist/code-blocks/remarkDetype.js +164 -0
  21. package/dist/code-blocks/shikiTransformerAutoLinks.d.ts +8 -0
  22. package/dist/code-blocks/shikiTransformerAutoLinks.js +51 -0
  23. package/dist/components/CodeBlockTransformer.js +2 -3
  24. package/dist/components/Comment.js +1 -2
  25. package/dist/components/FileRemoved.js +4 -6
  26. package/dist/components/HorizontalLine.js +1 -2
  27. package/dist/components/ImportMeta.js +2 -3
  28. package/dist/components/Link.js +34 -50
  29. package/dist/components/Note.js +17 -29
  30. package/dist/components/P.js +1 -12
  31. package/dist/components/RepoLink.js +7 -9
  32. package/dist/determineNavItemsColumnLayout.js +48 -63
  33. package/dist/parseMarkdownMini.js +5 -17
  34. package/dist/parsePageSections.js +41 -82
  35. package/dist/renderer/usePageContext.js +6 -7
  36. package/dist/resolvePageContext.js +103 -110
  37. package/dist/utils/Emoji/Emoji.js +13 -21
  38. package/dist/utils/assert.js +14 -16
  39. package/dist/utils/cls.js +1 -1
  40. package/dist/utils/determineSectionUrlHash.js +5 -5
  41. package/dist/utils/filter.js +2 -2
  42. package/dist/utils/getGlobalObject.js +3 -3
  43. package/dist/vite.config.js +17 -7
  44. package/index.ts +16 -14
  45. package/package.json +7 -2
  46. package/resolvePageContext.ts +19 -15
  47. package/tsconfig.json +2 -1
  48. package/vite.config.ts +14 -4
package/css/button.css CHANGED
@@ -5,3 +5,26 @@ button,
5
5
  border-radius: 5px;
6
6
  cursor: pointer;
7
7
  }
8
+
9
+ /* Raised button */
10
+ .raised {
11
+ cursor: pointer;
12
+ border-radius: 5px;
13
+ border-style: solid;
14
+ border-width: 1px 2px 2px 1px;
15
+ border-color: hsl(0, 0%, 75%) hsl(0, 0%, 72%) hsl(0, 0%, 72%) hsl(0, 0%, 75%);
16
+
17
+ &:hover {
18
+ border-color: hsl(0, 0%, 72%) hsl(0, 0%, 66%) hsl(0, 0%, 66%) hsl(0, 0%, 72%);
19
+ }
20
+
21
+ &:active {
22
+ border-width: 2px 1px 1px 2px;
23
+ border-color: hsl(0, 0%, 66%) hsl(0, 0%, 72%) hsl(0, 0%, 72%) hsl(0, 0%, 66%);
24
+ }
25
+
26
+ &:disabled {
27
+ border-width: 1px;
28
+ border-color: hsl(0, 0%, 72%);
29
+ }
30
+ }
package/css/code.css CHANGED
@@ -2,29 +2,11 @@
2
2
  @import './code/block.css';
3
3
  @import './code/diff.css';
4
4
 
5
- code {
6
- border-radius: 4px;
7
- }
8
- pre {
9
- background: none !important;
10
- }
5
+ /* For code blocks, see Pre.css instead */
11
6
 
12
- /* Inline */
7
+ /* Inline <code> */
13
8
  code {
9
+ border-radius: 4px;
14
10
  background: rgba(0, 0, 0, 0.063137255);
15
11
  font-size: 1.1em;
16
12
  }
17
-
18
- /* Block */
19
- pre > code {
20
- /*
21
- background-color: #f4f4f4;
22
- 0.043137255 = 1 - (#f4 / #ff)
23
- */
24
- background: rgba(0, 0, 0, 0.043137255);
25
- font-size: 1em;
26
- }
27
- /* Workaround for shiki regression */
28
- pre > code:not([data-language]) {
29
- padding: 16px !important;
30
- }
package/css/tooltip.css CHANGED
@@ -17,10 +17,8 @@
17
17
  font-size: 12px;
18
18
  content: attr(aria-label);
19
19
  position: absolute;
20
- top: 100%;
21
20
  left: 50%;
22
21
  transform: translate(-50%, 0);
23
- margin-top: 5px;
24
22
  background: #fdfdfd;
25
23
  padding: 3px 10px;
26
24
  box-shadow: rgb(0 0 0 / 8%) 2px 4px 7px 0px;
@@ -34,4 +32,14 @@
34
32
  */
35
33
  white-space: nowrap;
36
34
  }
35
+ /* Show below */
36
+ [aria-label]:not([data-label-position])::before {
37
+ top: 100%;
38
+ margin-top: 5px;
39
+ }
40
+ /* Show above */
41
+ [aria-label][data-label-position='top']::before {
42
+ bottom: 100%;
43
+ margin-bottom: 7px;
44
+ }
37
45
  }
package/dist/+config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { config as default };
2
2
  import { viteConfig } from './vite.config.js';
3
- var config = {
3
+ const config = {
4
4
  name: '@brillout/docpress',
5
5
  require: { vike: '>=0.4.234' },
6
6
  vite: viteConfig,
@@ -1,53 +1,39 @@
1
- var __assign = (this && this.__assign) || function () {
2
- __assign = Object.assign || function(t) {
3
- for (var s, i = 1, n = arguments.length; i < n; i++) {
4
- s = arguments[i];
5
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
- t[p] = s[p];
7
- }
8
- return t;
9
- };
10
- return __assign.apply(this, arguments);
11
- };
12
1
  export { NavItemComponent };
13
2
  export { getNavItemsWithComputed };
14
3
  import React from 'react';
15
4
  import { assert, assertWarning, jsxToTextContent } from './utils/server';
16
5
  import './NavItemComponent.css';
17
6
  import { parseMarkdownMini } from './parseMarkdownMini';
18
- function NavItemComponent(_a) {
19
- var _b;
20
- var _c;
21
- var navItem = _a.navItem, onClick = _a.onClick;
7
+ function NavItemComponent({ navItem, onClick, }) {
22
8
  assert([1, 2, 3, 4].includes(navItem.level), navItem);
23
- var titleJsx = parseMarkdownMini(navItem.title);
24
- var titleInNavJsx = parseMarkdownMini(navItem.titleInNav);
25
- var iconSize = 25;
26
- var icon = navItem.titleIcon && (React.createElement("img", { src: navItem.titleIcon, style: __assign({ height: iconSize, width: iconSize, marginRight: 8, marginLeft: 4 }, navItem.titleIconStyle) }));
9
+ const titleJsx = parseMarkdownMini(navItem.title);
10
+ const titleInNavJsx = parseMarkdownMini(navItem.titleInNav);
11
+ const iconSize = 25;
12
+ const icon = navItem.titleIcon && (React.createElement("img", { src: navItem.titleIcon, style: { height: iconSize, width: iconSize, marginRight: 8, marginLeft: 4, ...navItem.titleIconStyle } }));
27
13
  if (navItem.level === 1 || navItem.level === 4) {
28
14
  assert(navItem.url === undefined);
29
15
  }
30
16
  else {
31
- var sectionTitle = jsxToTextContent(titleJsx);
17
+ const sectionTitle = jsxToTextContent(titleJsx);
32
18
  assertWarning(navItem.url, [
33
- "".concat(jsxToTextContent(titleInNavJsx), " is missing a URL hash."),
34
- "Add a URL hash with: `## ".concat(sectionTitle, "{#some-hash}`."),
19
+ `${jsxToTextContent(titleInNavJsx)} is missing a URL hash.`,
20
+ `Add a URL hash with: \`## ${sectionTitle}{#some-hash}\`.`,
35
21
  /* TO-DO/eventually: not implemented yet.
36
22
  `Use \`<h2 id="url-hash">${sectionTitle}</h2>\` instead of \`## ${sectionTitle}\`.`,
37
23
  */
38
24
  ].join(' '));
39
25
  }
40
- var children = titleInNavJsx;
26
+ let children = titleInNavJsx;
41
27
  if (navItem.level === 1) {
42
28
  children = (React.createElement(React.Fragment, null,
43
29
  icon,
44
30
  children,
45
31
  React.createElement(Chevron, { className: "collapsible-icon", height: 9 })));
46
32
  }
47
- var props = {
48
- href: (_c = navItem.url) !== null && _c !== void 0 ? _c : undefined,
49
- children: children,
50
- onClick: onClick,
33
+ const props = {
34
+ href: navItem.url ?? undefined,
35
+ children,
36
+ onClick,
51
37
  className: [
52
38
  'nav-item',
53
39
  'nav-item-level-' + navItem.level,
@@ -59,45 +45,51 @@ function NavItemComponent(_a) {
59
45
  .join(' '),
60
46
  };
61
47
  if (navItem.level === 1) {
62
- props.style = (_b = {},
63
- _b['--category-color'] = navItem.color,
64
- _b);
48
+ props.style = {
49
+ ['--category-color']: navItem.color,
50
+ };
65
51
  }
66
52
  if (navItem.level === 2 || navItem.level === 3) {
67
- return React.createElement("a", __assign({}, props));
53
+ return React.createElement("a", { ...props });
68
54
  }
69
55
  else {
70
- return React.createElement("span", __assign({}, props));
56
+ return React.createElement("span", { ...props });
71
57
  }
72
58
  }
73
59
  function getNavItemsWithComputed(navItems, currentUrl) {
74
- var navItemIdx;
75
- var navItemsWithComputed = navItems.map(function (navItem, i) {
60
+ let navItemIdx;
61
+ const navItemsWithComputed = navItems.map((navItem, i) => {
76
62
  assert([1, 2, 3, 4].includes(navItem.level), navItem);
77
- var navItemPrevious = navItems[i - 1];
78
- var navItemNext = navItems[i + 1];
79
- var isActive = false;
63
+ const navItemPrevious = navItems[i - 1];
64
+ const navItemNext = navItems[i + 1];
65
+ let isActive = false;
80
66
  if (navItem.url === currentUrl) {
81
- assert(navItem.level === 2, { currentUrl: currentUrl });
67
+ assert(navItem.level === 2, { currentUrl });
82
68
  assert(navItemIdx === undefined);
83
69
  navItemIdx = i;
84
70
  isActive = true;
85
71
  }
86
- var isFirstOfItsKind = navItem.level !== (navItemPrevious === null || navItemPrevious === void 0 ? void 0 : navItemPrevious.level);
87
- var isLastOfItsKind = navItem.level !== (navItemNext === null || navItemNext === void 0 ? void 0 : navItemNext.level);
88
- var navItemComputed = __assign(__assign({}, navItem), { isActive: isActive, isRelevant: false, isFirstOfItsKind: isFirstOfItsKind, isLastOfItsKind: isLastOfItsKind });
72
+ const isFirstOfItsKind = navItem.level !== navItemPrevious?.level;
73
+ const isLastOfItsKind = navItem.level !== navItemNext?.level;
74
+ const navItemComputed = {
75
+ ...navItem,
76
+ isActive,
77
+ isRelevant: false,
78
+ isFirstOfItsKind,
79
+ isLastOfItsKind,
80
+ };
89
81
  return navItemComputed;
90
82
  });
91
83
  // Set `isRelevant`
92
84
  if (navItemIdx !== undefined) {
93
- for (var i = navItemIdx; i >= 0; i--) {
94
- var navItem = navItemsWithComputed[i];
85
+ for (let i = navItemIdx; i >= 0; i--) {
86
+ const navItem = navItemsWithComputed[i];
95
87
  navItem.isRelevant = true;
96
88
  if (navItem.level === 1)
97
89
  break;
98
90
  }
99
- for (var i = navItemIdx; i < navItemsWithComputed.length; i++) {
100
- var navItem = navItemsWithComputed[i];
91
+ for (let i = navItemIdx; i < navItemsWithComputed.length; i++) {
92
+ const navItem = navItemsWithComputed[i];
101
93
  if (navItem.level === 1)
102
94
  break;
103
95
  navItem.isRelevant = true;
@@ -106,6 +98,6 @@ function getNavItemsWithComputed(navItems, currentUrl) {
106
98
  return navItemsWithComputed;
107
99
  }
108
100
  function Chevron(props) {
109
- return (React.createElement("svg", __assign({ viewBox: "0 0 512 292.52", xmlns: "http://www.w3.org/2000/svg" }, props),
101
+ return (React.createElement("svg", { viewBox: "0 0 512 292.52", xmlns: "http://www.w3.org/2000/svg", ...props },
110
102
  React.createElement("path", { fill: "#aaa", d: "M10.725 82.42L230.125 261.82c6.8 6.8 16.2 10.7 25.9 10.7s19.1-3.9 25.9-10.7l219.4-179.4c14.3-14.3 14.3-37.4 0-51.7s-37.4-14.3-51.7 0l-193.6 153.6-193.6-153.6c-14.3-14.3-37.4-14.3-51.7 0s-14.3 37.5 0 51.7z" })));
111
103
  }
@@ -0,0 +1,35 @@
1
+ export { rehypeMetaToProps, parseMetaString };
2
+ import type { ElementData, Root } from 'hast';
3
+ /**
4
+ * Rehype plugin to extract metadata from `<code>` blocks in markdown
5
+ * and attach them as props to the parent `<pre>` element.
6
+ *
7
+ * This allows using those props inside a custom `<Pre>` component.
8
+ *
9
+ * Example:
10
+ * ~~~mdx
11
+ * ```js foo="bar" hide_copy='true'
12
+ * export function add(a, b) {
13
+ * return a + b
14
+ * }
15
+ * ```
16
+ * ~~~
17
+ * These props are then added to the `<pre>` element
18
+ */
19
+ declare function rehypeMetaToProps(): (tree: Root) => void;
20
+ /**
21
+ * Minimal parser for a metadata string into key-value pairs.
22
+ *
23
+ * Supports simple patterns: key or key="value".
24
+ *
25
+ * Keys must contain only letters, dashes, or underscores (no digits).
26
+ * Keys are converted to kebab-case. Values default to "true" if missing.
27
+ *
28
+ * Example:
29
+ * parseMetaString('foo fooBar="value"')
30
+ * => { foo: 'true', foo_bar: 'value' }
31
+ *
32
+ * @param metaString - The input metadata string.
33
+ * @returns A plain object of parsed key-value pairs.
34
+ */
35
+ declare function parseMetaString(metaString: ElementData['meta']): Record<string, string>;
@@ -0,0 +1,62 @@
1
+ export { rehypeMetaToProps, parseMetaString };
2
+ import { visit } from 'unist-util-visit';
3
+ /**
4
+ * Rehype plugin to extract metadata from `<code>` blocks in markdown
5
+ * and attach them as props to the parent `<pre>` element.
6
+ *
7
+ * This allows using those props inside a custom `<Pre>` component.
8
+ *
9
+ * Example:
10
+ * ~~~mdx
11
+ * ```js foo="bar" hide_copy='true'
12
+ * export function add(a, b) {
13
+ * return a + b
14
+ * }
15
+ * ```
16
+ * ~~~
17
+ * These props are then added to the `<pre>` element
18
+ */
19
+ function rehypeMetaToProps() {
20
+ return (tree) => {
21
+ visit(tree, 'element', (node, _index, parent) => {
22
+ if (node.tagName === 'code' && parent?.type === 'element' && parent.tagName === 'pre') {
23
+ const props = parseMetaString(node.data?.meta);
24
+ parent.properties ??= {};
25
+ parent.properties = { ...parent.properties, ...props };
26
+ }
27
+ });
28
+ };
29
+ }
30
+ /**
31
+ * Minimal parser for a metadata string into key-value pairs.
32
+ *
33
+ * Supports simple patterns: key or key="value".
34
+ *
35
+ * Keys must contain only letters, dashes, or underscores (no digits).
36
+ * Keys are converted to kebab-case. Values default to "true" if missing.
37
+ *
38
+ * Example:
39
+ * parseMetaString('foo fooBar="value"')
40
+ * => { foo: 'true', foo_bar: 'value' }
41
+ *
42
+ * @param metaString - The input metadata string.
43
+ * @returns A plain object of parsed key-value pairs.
44
+ */
45
+ function parseMetaString(metaString) {
46
+ if (!metaString)
47
+ return {};
48
+ const props = {};
49
+ const keyValuePairRE = /([a-zA-Z_-]+)(?:=([^"'\s]+))?(?=\s|$)/g;
50
+ for (const match of metaString.matchAll(keyValuePairRE)) {
51
+ let [_, key, value] = match;
52
+ props[kebabCase(key)] = value || 'true';
53
+ }
54
+ return props;
55
+ }
56
+ // Simple function to convert a camelCase or PascalCase string to kebab-case.
57
+ function kebabCase(str) {
58
+ return str
59
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
60
+ .replace('_', '-')
61
+ .toLowerCase();
62
+ }
@@ -0,0 +1,4 @@
1
+ export { remarkDetype };
2
+ import type { Root } from 'mdast';
3
+ import type { VFile } from '@mdx-js/mdx/internal-create-format-aware-processors';
4
+ declare function remarkDetype(): (tree: Root, file: VFile) => Promise<void>;
@@ -0,0 +1,164 @@
1
+ export { remarkDetype };
2
+ import { visit } from 'unist-util-visit';
3
+ import { assertUsage } from '../utils/assert.js';
4
+ import { parseMetaString } from './rehypeMetaToProps.js';
5
+ import pc from '@brillout/picocolors';
6
+ import module from 'node:module';
7
+ // Cannot use `import { transform } from 'detype'` as it results in errors,
8
+ // and the package has no default export. Using `module.createRequire` instead.
9
+ const { transform: detype } = module.createRequire(import.meta.url)('detype');
10
+ const prettierOptions = {
11
+ semi: false,
12
+ singleQuote: true,
13
+ trailingComma: 'none',
14
+ };
15
+ function remarkDetype() {
16
+ return async function transformer(tree, file) {
17
+ const code_nodes = [];
18
+ visit(tree, 'code', (node, index, parent) => {
19
+ if (!parent || typeof index === 'undefined')
20
+ return;
21
+ // Skip if `node.lang` is not ts, tsx, vue, or yaml
22
+ if (!['ts', 'tsx', 'vue', 'yaml'].includes(node.lang || ''))
23
+ return;
24
+ // Skip if 'ts-only' meta is present
25
+ if (node.meta?.includes('ts-only'))
26
+ return;
27
+ // Collect this node for post-processing
28
+ code_nodes.push({ codeBlock: node, index, parent });
29
+ });
30
+ for (const node of code_nodes.reverse()) {
31
+ if (node.codeBlock.lang === 'yaml') {
32
+ transformYaml(node);
33
+ }
34
+ else {
35
+ await transformTsToJs(node, file);
36
+ }
37
+ }
38
+ };
39
+ }
40
+ function transformYaml(node) {
41
+ const { codeBlock, index, parent } = node;
42
+ const codeBlockContentJs = replaceFileNameSuffixes(codeBlock.value);
43
+ // Skip wrapping if the YAML code block hasn't changed
44
+ if (codeBlockContentJs === codeBlock.value)
45
+ return;
46
+ const { position, ...rest } = codeBlock;
47
+ // Create a new code node for the JS version based on the modified YAML
48
+ const yamlJsCode = {
49
+ ...rest,
50
+ value: codeBlockContentJs,
51
+ };
52
+ // Wrap both the original YAML and `yamlJsCode` with <CodeSnippets>
53
+ const yamlContainer = {
54
+ type: 'mdxJsxFlowElement',
55
+ name: 'CodeSnippets',
56
+ children: [yamlJsCode, codeBlock],
57
+ attributes: [
58
+ {
59
+ name: 'hideToggle',
60
+ type: 'mdxJsxAttribute',
61
+ },
62
+ ],
63
+ };
64
+ parent.children.splice(index, 1, yamlContainer);
65
+ }
66
+ async function transformTsToJs(node, file) {
67
+ const { codeBlock, index, parent } = node;
68
+ const maxWidth = Number(parseMetaString(codeBlock.meta)['max-width']);
69
+ let codeBlockReplacedJs = replaceFileNameSuffixes(codeBlock.value);
70
+ let codeBlockContentJs = '';
71
+ // Remove TypeScript from the TS/TSX/Vue code node
72
+ try {
73
+ codeBlockContentJs = await detype(codeBlockReplacedJs, `some-dummy-filename.${codeBlock.lang}`, {
74
+ customizeBabelConfig(config) {
75
+ // Add `onlyRemoveTypeImports: true` to the internal `@babel/preset-typescript` config
76
+ // https://github.com/cyco130/detype/blob/46ec867e9efd31d31a312a215ca169bd6bff4726/src/transform.ts#L206
77
+ assertUsage(config.presets && config.presets.length === 1, 'Unexpected Babel config presets');
78
+ config.presets = [[config.presets[0], { onlyRemoveTypeImports: true }]];
79
+ },
80
+ removeTsComments: true,
81
+ prettierOptions: {
82
+ ...prettierOptions,
83
+ printWidth: maxWidth ? maxWidth : 99,
84
+ },
85
+ });
86
+ }
87
+ catch (error) {
88
+ // Log errors and return original content instead of throwing
89
+ console.error(pc.red(error.message));
90
+ console.error([
91
+ `Failed to transform the code block in: ${pc.bold(pc.blue(file.path))}.`,
92
+ "This likely happened due to invalid TypeScript syntax (see detype's error message above). You can either:",
93
+ '- Fix the code block syntax',
94
+ '- Set the code block language to js instead of ts',
95
+ '- Use custom meta or comments https://github.com/brillout/docpress#detype',
96
+ ].join('\n') + '\n');
97
+ return;
98
+ }
99
+ // Clean up both JS and TS code contents: correct diff comments (for js only) and apply custom magic comment replacements
100
+ codeBlockContentJs = cleanUpCode(codeBlockContentJs.trimEnd(), true);
101
+ codeBlock.value = cleanUpCode(codeBlock.value);
102
+ // No wrapping needed if JS and TS code are still identical
103
+ if (codeBlockContentJs === codeBlock.value)
104
+ return;
105
+ const { position, lang, ...rest } = codeBlock;
106
+ const attributes = [];
107
+ const jsCode = {
108
+ ...rest,
109
+ // The jsCode lang should be js|jsx|vue
110
+ lang: lang.replace('t', 'j'),
111
+ value: codeBlockContentJs,
112
+ };
113
+ // Add `hideToggle` attribute (prop) to `CodeSnippets` if the only change was replacing `.ts` with `.js`
114
+ if (codeBlockReplacedJs === codeBlockContentJs) {
115
+ attributes.push({
116
+ name: 'hideToggle',
117
+ type: 'mdxJsxAttribute',
118
+ });
119
+ }
120
+ // Wrap both the original `codeBlock` and `jsCode` with <CodeSnippets>
121
+ const container = {
122
+ type: 'mdxJsxFlowElement',
123
+ name: 'CodeSnippets',
124
+ children: [jsCode, codeBlock],
125
+ attributes,
126
+ };
127
+ parent.children.splice(index, 1, container);
128
+ }
129
+ // Replace all '.ts' extensions with '.js'
130
+ function replaceFileNameSuffixes(codeBlockValue) {
131
+ return codeBlockValue.replaceAll('.ts', '.js');
132
+ }
133
+ function cleanUpCode(code, isJsCode = false) {
134
+ if (isJsCode) {
135
+ code = correctCodeDiffComments(code);
136
+ }
137
+ return processMagicComments(code);
138
+ }
139
+ function processMagicComments(code) {
140
+ // @detype-replace DummyLayout Layout
141
+ const renameCommentRE = /^\/\/\s@detype-replace\s([^ ]+) ([^ ]+)\n/gm;
142
+ const matches = Array.from(code.matchAll(renameCommentRE));
143
+ if (matches.length) {
144
+ for (let i = matches.length - 1; i >= 0; i--) {
145
+ const match = matches[i];
146
+ const [fullMatch, renameFrom, renameTo] = match;
147
+ code = code.split(fullMatch).join('').replaceAll(renameFrom, renameTo);
148
+ }
149
+ }
150
+ code = code.replaceAll('// @detype-uncomment ', '');
151
+ return code;
152
+ }
153
+ /**
154
+ * Correct code diff comments that detype() unexpectedly reformatted (using Prettier and Babel internally)
155
+ * after removing TypeScript.
156
+ * See https://github.com/brillout/docpress/pull/47#issuecomment-3263953899
157
+ * @param code Transformed Javascript code.
158
+ * @returns The corrected code.
159
+ */
160
+ function correctCodeDiffComments(code) {
161
+ // Expected to match the code diff comments: `// [!code ++]` and `// [!code --]` started with newline and optional spaces
162
+ const codeDiffRE = /\n\s*\/\/\s\[!code.+\]/g;
163
+ return code.replaceAll(codeDiffRE, (codeDiff) => codeDiff.trimStart());
164
+ }
@@ -0,0 +1,8 @@
1
+ export { shikiTransformerAutoLinks };
2
+ import type { ShikiTransformer } from 'shiki';
3
+ /**
4
+ * A Shiki transformer that converts plain HTTPS URLs in code blocks into clickable `<a>` links.
5
+ *
6
+ * Inspired by `@jcayzac/shiki-transformer-autolinks`, but tailored for a narrower use case.
7
+ */
8
+ declare function shikiTransformerAutoLinks(): ShikiTransformer;
@@ -0,0 +1,51 @@
1
+ export { shikiTransformerAutoLinks };
2
+ const linkRE = /https:\/\/[^\s]*[^.,\s"'`]/g;
3
+ /**
4
+ * A Shiki transformer that converts plain HTTPS URLs in code blocks into clickable `<a>` links.
5
+ *
6
+ * Inspired by `@jcayzac/shiki-transformer-autolinks`, but tailored for a narrower use case.
7
+ */
8
+ function shikiTransformerAutoLinks() {
9
+ return {
10
+ name: 'docpress-shiki-autolinks',
11
+ span(span) {
12
+ if (span.children.length !== 1)
13
+ return;
14
+ let child = span.children[0];
15
+ if (child.type !== 'text')
16
+ return;
17
+ const links = [];
18
+ const matches = Array.from(child.value.matchAll(linkRE));
19
+ // Filter out URLs that contain `${...}`. e.g. `https://star-wars.brillout.com/api/films/${id}.json`.
20
+ const filtered = matches.filter(([href]) => !href.includes('${'));
21
+ if (filtered.length === 0)
22
+ return;
23
+ for (const match of filtered) {
24
+ const [href] = match;
25
+ links.unshift({ href, index: match.index });
26
+ }
27
+ const newChildren = [];
28
+ for (const { href, index } of links) {
29
+ const postIndex = index + href.length;
30
+ const postValue = child.value.slice(postIndex);
31
+ if (postValue.length > 0) {
32
+ newChildren.unshift({ type: 'text', value: postValue });
33
+ }
34
+ newChildren.unshift({
35
+ type: 'element',
36
+ tagName: 'a',
37
+ properties: { href },
38
+ children: [{ type: 'text', value: href }],
39
+ });
40
+ child = {
41
+ type: 'text',
42
+ value: child.value.slice(0, index),
43
+ };
44
+ }
45
+ if (child.value.length > 0) {
46
+ newChildren.unshift(child);
47
+ }
48
+ span.children = newChildren;
49
+ },
50
+ };
51
+ }
@@ -2,9 +2,8 @@ export { CodeBlockTransformer };
2
2
  import React from 'react';
3
3
  import { assert } from '../utils/server';
4
4
  import './CodeBlockTransformer.css';
5
- function CodeBlockTransformer(_a) {
6
- var children = _a.children, lineBreak = _a.lineBreak;
5
+ function CodeBlockTransformer({ children, lineBreak }) {
7
6
  assert(lineBreak === 'white-space' || lineBreak === 'break-word', '`lineBreak` is currently the only use case for <CodeBlockTransformer>');
8
- var className = "with-line-break_".concat(lineBreak);
7
+ const className = `with-line-break_${lineBreak}`;
9
8
  return React.createElement("div", { className: className }, children);
10
9
  }
@@ -1,6 +1,5 @@
1
1
  export { Comment };
2
2
  import React from 'react';
3
- function Comment(_a) {
4
- var children = _a.children;
3
+ function Comment({ children }) {
5
4
  return React.createElement(React.Fragment, null);
6
5
  }
@@ -2,25 +2,23 @@ export { FileRemoved };
2
2
  export { FileAdded };
3
3
  import React from 'react';
4
4
  // Styling defined in src/css/code/diff.css
5
- var classRemoved = [
5
+ const classRemoved = [
6
6
  //
7
7
  'diff-entire-file',
8
8
  'diff-entire-file-removed',
9
9
  ].join(' ');
10
- var classAdded = [
10
+ const classAdded = [
11
11
  //
12
12
  'diff-entire-file',
13
13
  'diff-entire-file-added',
14
14
  ].join(' ');
15
- function FileRemoved(_a) {
16
- var children = _a.children;
15
+ function FileRemoved({ children }) {
17
16
  return React.createElement("div", { className: classRemoved },
18
17
  " ",
19
18
  children,
20
19
  " ");
21
20
  }
22
- function FileAdded(_a) {
23
- var children = _a.children;
21
+ function FileAdded({ children }) {
24
22
  return React.createElement("div", { className: classAdded },
25
23
  " ",
26
24
  children,
@@ -1,8 +1,7 @@
1
1
  export { HorizontalLine };
2
2
  import React from 'react';
3
3
  import { cls } from '../utils/cls';
4
- function HorizontalLine(_a) {
5
- var primary = _a.primary;
4
+ function HorizontalLine({ primary }) {
6
5
  return (React.createElement("div", { className: cls(primary && 'primary'), style: { textAlign: 'center' } },
7
6
  React.createElement("hr", { style: {
8
7
  display: 'inline-block',
@@ -1,10 +1,9 @@
1
1
  import React from 'react';
2
2
  import { assert } from '../utils/server';
3
3
  export { ImportMeta };
4
- function ImportMeta(_a) {
5
- var prop = _a.prop;
4
+ function ImportMeta({ prop }) {
6
5
  assert(!prop.startsWith('import'));
7
6
  assert(!prop.startsWith('.'));
8
- var text = 'imp' + 'ort.meta.' + prop;
7
+ const text = 'imp' + 'ort.meta.' + prop;
9
8
  return React.createElement("code", null, text);
10
9
  }