@brillout/docpress 0.15.9 → 0.15.10-commit-b6b1605
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/NavItemComponent.tsx +12 -0
- package/components/CodeSnippets.tsx +72 -0
- package/components/index.ts +1 -0
- package/detypePlugin.ts +112 -0
- package/dist/components/CodeSnippets.d.ts +13 -0
- package/dist/components/CodeSnippets.js +101 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/detypePlugin.d.ts +3 -0
- package/dist/detypePlugin.js +156 -0
- package/dist/utils/contentMap.d.ts +9 -0
- package/dist/utils/contentMap.js +22 -0
- package/dist/utils/useSelectedLanguage.d.ts +7 -0
- package/dist/utils/useSelectedLanguage.js +49 -0
- package/dist/vite.config.js +2 -0
- package/global.d.ts +2 -0
- package/index.ts +3 -0
- package/package.json +2 -1
- package/utils/contentMap.ts +34 -0
- package/utils/useSelectedLanguage.ts +61 -0
- package/vite.config.ts +2 -0
package/NavItemComponent.tsx
CHANGED
|
@@ -9,6 +9,18 @@ import { assert, assertWarning, jsxToTextContent } from './utils/server'
|
|
|
9
9
|
import './NavItemComponent.css'
|
|
10
10
|
import { parseMarkdownMini } from './parseMarkdownMini'
|
|
11
11
|
|
|
12
|
+
/*
|
|
13
|
+
// We cannot do that: we must use `import type` otherwise Vite will transpile global.d.ts and throw:
|
|
14
|
+
// ```console
|
|
15
|
+
// [11:55:47.528][/docs/.test-dev.test.ts][pnpm run dev][stderr] 11:55:47 AM [vite] Failed to transpile /home/runner/work/telefunc/telefunc/node_modules/.pnpm/@brillout+docpress@0.15.7_@algolia+client-search@5.31.0_@types+react@19.1.8_@vitejs+plugin-re_lcm3fspejcg3ebrmr3gvb5i3se/node_modules/@brillout/docpress/global.d.ts because:
|
|
16
|
+
// x `declare` modifier not allowed for code already in an ambient context
|
|
17
|
+
// ```
|
|
18
|
+
import './global.d.ts'
|
|
19
|
+
/*/
|
|
20
|
+
// The only purpose of `FakeExport` is to be able to use `import type`
|
|
21
|
+
import type { FakeExport } from './global.d.ts'
|
|
22
|
+
//*/
|
|
23
|
+
|
|
12
24
|
type NavItemComputed = ReturnType<typeof getNavItemsWithComputed>[number]
|
|
13
25
|
type NavItem = {
|
|
14
26
|
level: number
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export { CodeSnippets, CodeSnippet, TypescriptOnly }
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { useSelectedLanguage } from '../utils/useSelectedLanguage'
|
|
5
|
+
|
|
6
|
+
function CodeSnippets({ children }: { children: React.ReactNode }) {
|
|
7
|
+
const [selectedLang, setSelectedLang] = useSelectedLanguage()
|
|
8
|
+
|
|
9
|
+
const handleOnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
10
|
+
setSelectedLang(e.target.value)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div>
|
|
15
|
+
<form style={{ position: 'relative' }}>
|
|
16
|
+
<select
|
|
17
|
+
name="language"
|
|
18
|
+
id="language"
|
|
19
|
+
onChange={handleOnChange}
|
|
20
|
+
value={selectedLang}
|
|
21
|
+
style={{ position: 'absolute', top: '10px', right: '60px', zIndex: 3 }}
|
|
22
|
+
>
|
|
23
|
+
<option value="js">Javascript</option>
|
|
24
|
+
<option value="ts">Typescript</option>
|
|
25
|
+
</select>
|
|
26
|
+
</form>
|
|
27
|
+
{children}
|
|
28
|
+
</div>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function CodeSnippet({
|
|
33
|
+
children,
|
|
34
|
+
language,
|
|
35
|
+
tsOnly = false,
|
|
36
|
+
}: { children: React.ReactNode; language: string; tsOnly: boolean }) {
|
|
37
|
+
const [selectedLang] = useSelectedLanguage()
|
|
38
|
+
|
|
39
|
+
const style = tsOnly ? {} : { display: selectedLang === language ? 'block' : 'none' }
|
|
40
|
+
|
|
41
|
+
const copyToClipboard = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
42
|
+
try {
|
|
43
|
+
const figureEl = e.currentTarget.nextElementSibling
|
|
44
|
+
if (figureEl?.tagName === 'FIGURE') {
|
|
45
|
+
await navigator.clipboard.writeText(figureEl.textContent ?? '')
|
|
46
|
+
console.log('Copied to clipboard!')
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.warn('Copy failed', error)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div style={{ ...style, position: 'relative' }}>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
style={{ position: 'absolute', top: '10px', right: '10px', zIndex: 3 }}
|
|
58
|
+
onClick={copyToClipboard}
|
|
59
|
+
>
|
|
60
|
+
Copy
|
|
61
|
+
</button>
|
|
62
|
+
{children}
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Show/hide TypeScript sections (code and/or plain)
|
|
68
|
+
function TypescriptOnly({ children }: { children: React.ReactNode }) {
|
|
69
|
+
const [selectedLang] = useSelectedLanguage()
|
|
70
|
+
|
|
71
|
+
return <div style={{ display: selectedLang === 'ts' ? 'block' : 'none' }}>{children}</div>
|
|
72
|
+
}
|
package/components/index.ts
CHANGED
package/detypePlugin.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export { detypePlugin }
|
|
2
|
+
|
|
3
|
+
import type { PluginOption } from 'vite'
|
|
4
|
+
import module from 'node:module'
|
|
5
|
+
import { createContentMap, contentMapKeyRE, type ContentMap } from './utils/contentMap.js'
|
|
6
|
+
|
|
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 } = module.createRequire(import.meta.url)('detype') as typeof import('detype')
|
|
10
|
+
|
|
11
|
+
function detypePlugin(): PluginOption {
|
|
12
|
+
return {
|
|
13
|
+
name: '@brillout/docpress:detypePlugin',
|
|
14
|
+
enforce: 'pre',
|
|
15
|
+
transform: async (code: string, id: string) => {
|
|
16
|
+
if (!id.endsWith('+Page.mdx')) {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
const contentMap = createContentMap()
|
|
20
|
+
const codeNew = await transformCode(code, contentMap)
|
|
21
|
+
const replaced = replaceContent(codeNew, contentMapKeyRE, (match) => {
|
|
22
|
+
const content = contentMap.get(match[0])
|
|
23
|
+
if (!content) {
|
|
24
|
+
throw new Error('Content not found')
|
|
25
|
+
}
|
|
26
|
+
return content
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return replaced
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const codeBlockRE = /^([ \t]{0,3}>?[ \t]?)```(tsx?|vue)[^\n]*\n([\s\S]*?)```/gm
|
|
35
|
+
const prettierOptions = {
|
|
36
|
+
semi: false,
|
|
37
|
+
singleQuote: true,
|
|
38
|
+
printWidth: 120,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function transformCode(code: string, contentMap: ContentMap) {
|
|
42
|
+
const matches = Array.from(code.matchAll(codeBlockRE))
|
|
43
|
+
if (matches.length === 0) {
|
|
44
|
+
return code
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let codeNew = `import { CodeSnippets, CodeSnippet } from '@brillout/docpress';\n`
|
|
48
|
+
let lastIndex = 0
|
|
49
|
+
|
|
50
|
+
for (const match of matches) {
|
|
51
|
+
let [fullMatch, startsWith, lang, tsCode] = match
|
|
52
|
+
const tsOpeningCode = fullMatch.split('\n')[0].slice(startsWith.length)
|
|
53
|
+
|
|
54
|
+
const blockStart = match.index
|
|
55
|
+
const blockEnd = blockStart + fullMatch.length
|
|
56
|
+
|
|
57
|
+
codeNew += code.slice(lastIndex, blockStart)
|
|
58
|
+
|
|
59
|
+
if (startsWith.length > 0) {
|
|
60
|
+
tsCode = stripStarts(tsCode, startsWith)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (tsOpeningCode.includes('ts-only')) {
|
|
64
|
+
const key = contentMap.add('ts-code-snippet', fullMatch.length, fullMatch)
|
|
65
|
+
codeNew += `${startsWith}<CodeSnippet language={'ts'} tsOnly={'true'}>\n${key}\n${startsWith}</CodeSnippet>`
|
|
66
|
+
} else {
|
|
67
|
+
const jsCode = await transform(tsCode.replaceAll('.ts', '.js'), `tsCode.${lang}`, {
|
|
68
|
+
removeTsComments: true,
|
|
69
|
+
prettierOptions,
|
|
70
|
+
})
|
|
71
|
+
const jsLang = lang === 'vue' ? 'vue' : lang.replace('t', 'j') // ts => js | tsx => jsx
|
|
72
|
+
const jsOpeningCode = tsOpeningCode.replace(lang, jsLang)
|
|
73
|
+
const closing = `\`\`\``
|
|
74
|
+
|
|
75
|
+
const jsCodeSnippet = `<CodeSnippet language={'js'}>\n${jsOpeningCode}\n${jsCode}${closing}\n</CodeSnippet>`
|
|
76
|
+
const tsCodeSnippet = `<CodeSnippet language={'ts'}>\n${tsOpeningCode}\n${tsCode}${closing}\n</CodeSnippet>`
|
|
77
|
+
const codeSnippets = putBackStarts(`${tsCodeSnippet}\n${jsCodeSnippet}`, startsWith)
|
|
78
|
+
|
|
79
|
+
const key = contentMap.add(`ts-js-code-snippets`, codeSnippets.length, codeSnippets)
|
|
80
|
+
codeNew += `${startsWith}<CodeSnippets>\n${key}\n${startsWith}</CodeSnippets>`
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lastIndex = blockEnd
|
|
84
|
+
}
|
|
85
|
+
codeNew += code.slice(lastIndex)
|
|
86
|
+
|
|
87
|
+
return codeNew
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function stripStarts(code: string, startsWith: string) {
|
|
91
|
+
return code
|
|
92
|
+
.split('\n')
|
|
93
|
+
.map((line) => line.slice(startsWith.length))
|
|
94
|
+
.join('\n')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function putBackStarts(code: string, startsWith: string) {
|
|
98
|
+
if (!startsWith.length) {
|
|
99
|
+
return code
|
|
100
|
+
}
|
|
101
|
+
return code
|
|
102
|
+
.split('\n')
|
|
103
|
+
.map((line) => `${startsWith}${line}`)
|
|
104
|
+
.join('\n')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function replaceContent(input: string, re: RegExp, replacer: (match: RegExpMatchArray) => string): string {
|
|
108
|
+
const replacements = Array.from(input.matchAll(re), replacer)
|
|
109
|
+
let i = 0
|
|
110
|
+
|
|
111
|
+
return input.replace(re, () => replacements[i++])
|
|
112
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { CodeSnippets, CodeSnippet, TypescriptOnly };
|
|
2
|
+
import React from 'react';
|
|
3
|
+
declare function CodeSnippets({ children }: {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
}): React.JSX.Element;
|
|
6
|
+
declare function CodeSnippet({ children, language, tsOnly, }: {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
language: string;
|
|
9
|
+
tsOnly: boolean;
|
|
10
|
+
}): React.JSX.Element;
|
|
11
|
+
declare function TypescriptOnly({ children }: {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
22
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
23
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
24
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
25
|
+
function step(op) {
|
|
26
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
27
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
28
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
29
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
30
|
+
switch (op[0]) {
|
|
31
|
+
case 0: case 1: t = op; break;
|
|
32
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
33
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
34
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
35
|
+
default:
|
|
36
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
37
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
38
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
39
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
40
|
+
if (t[2]) _.ops.pop();
|
|
41
|
+
_.trys.pop(); continue;
|
|
42
|
+
}
|
|
43
|
+
op = body.call(thisArg, _);
|
|
44
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
45
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
export { CodeSnippets, CodeSnippet, TypescriptOnly };
|
|
49
|
+
import React from 'react';
|
|
50
|
+
import { useSelectedLanguage } from '../utils/useSelectedLanguage';
|
|
51
|
+
function CodeSnippets(_a) {
|
|
52
|
+
var children = _a.children;
|
|
53
|
+
var _b = useSelectedLanguage(), selectedLang = _b[0], setSelectedLang = _b[1];
|
|
54
|
+
var handleOnChange = function (e) {
|
|
55
|
+
setSelectedLang(e.target.value);
|
|
56
|
+
};
|
|
57
|
+
return (React.createElement("div", null,
|
|
58
|
+
React.createElement("form", { style: { position: 'relative' } },
|
|
59
|
+
React.createElement("select", { name: "language", id: "language", onChange: handleOnChange, value: selectedLang, style: { position: 'absolute', top: '10px', right: '60px', zIndex: 3 } },
|
|
60
|
+
React.createElement("option", { value: "js" }, "Javascript"),
|
|
61
|
+
React.createElement("option", { value: "ts" }, "Typescript"))),
|
|
62
|
+
children));
|
|
63
|
+
}
|
|
64
|
+
function CodeSnippet(_a) {
|
|
65
|
+
var _this = this;
|
|
66
|
+
var children = _a.children, language = _a.language, _b = _a.tsOnly, tsOnly = _b === void 0 ? false : _b;
|
|
67
|
+
var selectedLang = useSelectedLanguage()[0];
|
|
68
|
+
var style = tsOnly ? {} : { display: selectedLang === language ? 'block' : 'none' };
|
|
69
|
+
var copyToClipboard = function (e) { return __awaiter(_this, void 0, void 0, function () {
|
|
70
|
+
var figureEl, error_1;
|
|
71
|
+
var _a;
|
|
72
|
+
return __generator(this, function (_b) {
|
|
73
|
+
switch (_b.label) {
|
|
74
|
+
case 0:
|
|
75
|
+
_b.trys.push([0, 3, , 4]);
|
|
76
|
+
figureEl = e.currentTarget.nextElementSibling;
|
|
77
|
+
if (!((figureEl === null || figureEl === void 0 ? void 0 : figureEl.tagName) === 'FIGURE')) return [3 /*break*/, 2];
|
|
78
|
+
return [4 /*yield*/, navigator.clipboard.writeText((_a = figureEl.textContent) !== null && _a !== void 0 ? _a : '')];
|
|
79
|
+
case 1:
|
|
80
|
+
_b.sent();
|
|
81
|
+
console.log('Copied to clipboard!');
|
|
82
|
+
_b.label = 2;
|
|
83
|
+
case 2: return [3 /*break*/, 4];
|
|
84
|
+
case 3:
|
|
85
|
+
error_1 = _b.sent();
|
|
86
|
+
console.warn('Copy failed', error_1);
|
|
87
|
+
return [3 /*break*/, 4];
|
|
88
|
+
case 4: return [2 /*return*/];
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}); };
|
|
92
|
+
return (React.createElement("div", { style: __assign(__assign({}, style), { position: 'relative' }) },
|
|
93
|
+
React.createElement("button", { type: "button", style: { position: 'absolute', top: '10px', right: '10px', zIndex: 3 }, onClick: copyToClipboard }, "Copy"),
|
|
94
|
+
children));
|
|
95
|
+
}
|
|
96
|
+
// Show/hide TypeScript sections (code and/or plain)
|
|
97
|
+
function TypescriptOnly(_a) {
|
|
98
|
+
var children = _a.children;
|
|
99
|
+
var selectedLang = useSelectedLanguage()[0];
|
|
100
|
+
return React.createElement("div", { style: { display: selectedLang === 'ts' ? 'block' : 'none' } }, children);
|
|
101
|
+
}
|
package/dist/components/index.js
CHANGED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
export { detypePlugin };
|
|
38
|
+
import module from 'node:module';
|
|
39
|
+
import { createContentMap, contentMapKeyRE } from './utils/contentMap.js';
|
|
40
|
+
// Cannot use `import { transform } from 'detype'` as it results in errors,
|
|
41
|
+
// and the package has no default export. Using `module.createRequire` instead.
|
|
42
|
+
var transform = module.createRequire(import.meta.url)('detype').transform;
|
|
43
|
+
function detypePlugin() {
|
|
44
|
+
var _this = this;
|
|
45
|
+
return {
|
|
46
|
+
name: '@brillout/docpress:detypePlugin',
|
|
47
|
+
enforce: 'pre',
|
|
48
|
+
transform: function (code, id) { return __awaiter(_this, void 0, void 0, function () {
|
|
49
|
+
var contentMap, codeNew, replaced;
|
|
50
|
+
return __generator(this, function (_a) {
|
|
51
|
+
switch (_a.label) {
|
|
52
|
+
case 0:
|
|
53
|
+
if (!id.endsWith('+Page.mdx')) {
|
|
54
|
+
return [2 /*return*/];
|
|
55
|
+
}
|
|
56
|
+
contentMap = createContentMap();
|
|
57
|
+
return [4 /*yield*/, transformCode(code, contentMap)];
|
|
58
|
+
case 1:
|
|
59
|
+
codeNew = _a.sent();
|
|
60
|
+
replaced = replaceContent(codeNew, contentMapKeyRE, function (match) {
|
|
61
|
+
var content = contentMap.get(match[0]);
|
|
62
|
+
if (!content) {
|
|
63
|
+
throw new Error('Content not found');
|
|
64
|
+
}
|
|
65
|
+
return content;
|
|
66
|
+
});
|
|
67
|
+
return [2 /*return*/, replaced];
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}); },
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
var codeBlockRE = /^([ \t]{0,3}>?[ \t]?)```(tsx?|vue)[^\n]*\n([\s\S]*?)```/gm;
|
|
74
|
+
var prettierOptions = {
|
|
75
|
+
semi: false,
|
|
76
|
+
singleQuote: true,
|
|
77
|
+
printWidth: 120,
|
|
78
|
+
};
|
|
79
|
+
function transformCode(code, contentMap) {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
81
|
+
var matches, codeNew, lastIndex, _i, matches_1, match, fullMatch, startsWith, lang, tsCode, tsOpeningCode, blockStart, blockEnd, key, jsCode, jsLang, jsOpeningCode, closing, jsCodeSnippet, tsCodeSnippet, codeSnippets, key;
|
|
82
|
+
return __generator(this, function (_a) {
|
|
83
|
+
switch (_a.label) {
|
|
84
|
+
case 0:
|
|
85
|
+
matches = Array.from(code.matchAll(codeBlockRE));
|
|
86
|
+
if (matches.length === 0) {
|
|
87
|
+
return [2 /*return*/, code];
|
|
88
|
+
}
|
|
89
|
+
codeNew = "import { CodeSnippets, CodeSnippet } from '@brillout/docpress';\n";
|
|
90
|
+
lastIndex = 0;
|
|
91
|
+
_i = 0, matches_1 = matches;
|
|
92
|
+
_a.label = 1;
|
|
93
|
+
case 1:
|
|
94
|
+
if (!(_i < matches_1.length)) return [3 /*break*/, 6];
|
|
95
|
+
match = matches_1[_i];
|
|
96
|
+
fullMatch = match[0], startsWith = match[1], lang = match[2], tsCode = match[3];
|
|
97
|
+
tsOpeningCode = fullMatch.split('\n')[0].slice(startsWith.length);
|
|
98
|
+
blockStart = match.index;
|
|
99
|
+
blockEnd = blockStart + fullMatch.length;
|
|
100
|
+
codeNew += code.slice(lastIndex, blockStart);
|
|
101
|
+
if (startsWith.length > 0) {
|
|
102
|
+
tsCode = stripStarts(tsCode, startsWith);
|
|
103
|
+
}
|
|
104
|
+
if (!tsOpeningCode.includes('ts-only')) return [3 /*break*/, 2];
|
|
105
|
+
key = contentMap.add('ts-code-snippet', fullMatch.length, fullMatch);
|
|
106
|
+
codeNew += "".concat(startsWith, "<CodeSnippet language={'ts'} tsOnly={'true'}>\n").concat(key, "\n").concat(startsWith, "</CodeSnippet>");
|
|
107
|
+
return [3 /*break*/, 4];
|
|
108
|
+
case 2: return [4 /*yield*/, transform(tsCode.replaceAll('.ts', '.js'), "tsCode.".concat(lang), {
|
|
109
|
+
removeTsComments: true,
|
|
110
|
+
prettierOptions: prettierOptions,
|
|
111
|
+
})];
|
|
112
|
+
case 3:
|
|
113
|
+
jsCode = _a.sent();
|
|
114
|
+
jsLang = lang === 'vue' ? 'vue' : lang.replace('t', 'j') // ts => js | tsx => jsx
|
|
115
|
+
;
|
|
116
|
+
jsOpeningCode = tsOpeningCode.replace(lang, jsLang);
|
|
117
|
+
closing = "```";
|
|
118
|
+
jsCodeSnippet = "<CodeSnippet language={'js'}>\n".concat(jsOpeningCode, "\n").concat(jsCode).concat(closing, "\n</CodeSnippet>");
|
|
119
|
+
tsCodeSnippet = "<CodeSnippet language={'ts'}>\n".concat(tsOpeningCode, "\n").concat(tsCode).concat(closing, "\n</CodeSnippet>");
|
|
120
|
+
codeSnippets = putBackStarts("".concat(tsCodeSnippet, "\n").concat(jsCodeSnippet), startsWith);
|
|
121
|
+
key = contentMap.add("ts-js-code-snippets", codeSnippets.length, codeSnippets);
|
|
122
|
+
codeNew += "".concat(startsWith, "<CodeSnippets>\n").concat(key, "\n").concat(startsWith, "</CodeSnippets>");
|
|
123
|
+
_a.label = 4;
|
|
124
|
+
case 4:
|
|
125
|
+
lastIndex = blockEnd;
|
|
126
|
+
_a.label = 5;
|
|
127
|
+
case 5:
|
|
128
|
+
_i++;
|
|
129
|
+
return [3 /*break*/, 1];
|
|
130
|
+
case 6:
|
|
131
|
+
codeNew += code.slice(lastIndex);
|
|
132
|
+
return [2 /*return*/, codeNew];
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function stripStarts(code, startsWith) {
|
|
138
|
+
return code
|
|
139
|
+
.split('\n')
|
|
140
|
+
.map(function (line) { return line.slice(startsWith.length); })
|
|
141
|
+
.join('\n');
|
|
142
|
+
}
|
|
143
|
+
function putBackStarts(code, startsWith) {
|
|
144
|
+
if (!startsWith.length) {
|
|
145
|
+
return code;
|
|
146
|
+
}
|
|
147
|
+
return code
|
|
148
|
+
.split('\n')
|
|
149
|
+
.map(function (line) { return "".concat(startsWith).concat(line); })
|
|
150
|
+
.join('\n');
|
|
151
|
+
}
|
|
152
|
+
function replaceContent(input, re, replacer) {
|
|
153
|
+
var replacements = Array.from(input.matchAll(re), replacer);
|
|
154
|
+
var i = 0;
|
|
155
|
+
return input.replace(re, function () { return replacements[i++]; });
|
|
156
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type ContentMap = {
|
|
2
|
+
/**
|
|
3
|
+
* @returns key
|
|
4
|
+
*/
|
|
5
|
+
add(title: string, sourceLength: number, content: string): string;
|
|
6
|
+
get(key: string): string | undefined;
|
|
7
|
+
};
|
|
8
|
+
export declare const createContentMap: () => ContentMap;
|
|
9
|
+
export declare const contentMapKeyRE: RegExp;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
export var createContentMap = function () {
|
|
3
|
+
var map = new Map();
|
|
4
|
+
return {
|
|
5
|
+
add: function (title, length, content) {
|
|
6
|
+
var key = generateKey("".concat(title, "_").concat(length));
|
|
7
|
+
if (!map.has(key)) {
|
|
8
|
+
map.set(key, content);
|
|
9
|
+
}
|
|
10
|
+
return key;
|
|
11
|
+
},
|
|
12
|
+
get: function (key) {
|
|
13
|
+
var val = map.get(key);
|
|
14
|
+
return val;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
var generateKey = function (value) {
|
|
19
|
+
var hash = createHash('md5').update(value).digest('hex');
|
|
20
|
+
return "#_#_".concat(hash, "_#_#");
|
|
21
|
+
};
|
|
22
|
+
export var contentMapKeyRE = /#_#_[0-9a-fA-F]{32}_#_#/g;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export { useSelectedLanguage };
|
|
2
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
3
|
+
var key = 'docpress:selectedLang';
|
|
4
|
+
var defaultSsrLang = 'ts';
|
|
5
|
+
var defaultClientLang = 'js';
|
|
6
|
+
function useSelectedLanguage() {
|
|
7
|
+
var _a = useState(defaultSsrLang), selectedLang = _a[0], setSelectedLang = _a[1];
|
|
8
|
+
var getValue = function () {
|
|
9
|
+
var _a;
|
|
10
|
+
try {
|
|
11
|
+
return (_a = localStorage.getItem(key)) !== null && _a !== void 0 ? _a : defaultClientLang;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
console.warn('Error reading from localStorage:', error);
|
|
15
|
+
return defaultClientLang;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var setValue = useCallback(function (value) {
|
|
19
|
+
try {
|
|
20
|
+
window.localStorage.setItem(key, value);
|
|
21
|
+
setSelectedLang(value);
|
|
22
|
+
window.dispatchEvent(new CustomEvent('lang-storage'));
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.warn('Error setting localStorage:', error);
|
|
26
|
+
}
|
|
27
|
+
}, []);
|
|
28
|
+
useEffect(function () {
|
|
29
|
+
// Initial load from localStorage
|
|
30
|
+
setSelectedLang(getValue());
|
|
31
|
+
// Update language in current tab
|
|
32
|
+
var handleCustomEvent = function () {
|
|
33
|
+
setSelectedLang(getValue());
|
|
34
|
+
};
|
|
35
|
+
// Update language if changed in another tab
|
|
36
|
+
var handleNativeStorage = function (event) {
|
|
37
|
+
if (event.key === key) {
|
|
38
|
+
setSelectedLang(getValue());
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
window.addEventListener('lang-storage', handleCustomEvent);
|
|
42
|
+
window.addEventListener('storage', handleNativeStorage);
|
|
43
|
+
return function () {
|
|
44
|
+
window.removeEventListener('lang-storage', handleCustomEvent);
|
|
45
|
+
window.removeEventListener('storage', handleNativeStorage);
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
return [selectedLang, setValue];
|
|
49
|
+
}
|
package/dist/vite.config.js
CHANGED
|
@@ -2,6 +2,7 @@ export { config as viteConfig };
|
|
|
2
2
|
import mdx from '@mdx-js/rollup';
|
|
3
3
|
import react from '@vitejs/plugin-react-swc';
|
|
4
4
|
import { parsePageSections } from './parsePageSections.js';
|
|
5
|
+
import { detypePlugin } from './detypePlugin.js';
|
|
5
6
|
import rehypePrettyCode from 'rehype-pretty-code';
|
|
6
7
|
import remarkGfm from 'remark-gfm';
|
|
7
8
|
import { transformerNotationDiff } from '@shikijs/transformers';
|
|
@@ -13,6 +14,7 @@ var config = {
|
|
|
13
14
|
root: root,
|
|
14
15
|
plugins: [
|
|
15
16
|
parsePageSections(),
|
|
17
|
+
detypePlugin(),
|
|
16
18
|
mdx({ rehypePlugins: rehypePlugins, remarkPlugins: remarkPlugins }),
|
|
17
19
|
// @vitejs/plugin-react-swc needs to be added *after* the mdx plugins
|
|
18
20
|
react(),
|
package/global.d.ts
CHANGED
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brillout/docpress",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.10-commit-b6b1605",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@brillout/picocolors": "^1.0.10",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"@mdx-js/rollup": "3.0.1",
|
|
12
12
|
"@shikijs/transformers": "1.2.0",
|
|
13
13
|
"@vitejs/plugin-react-swc": "^3.10.2",
|
|
14
|
+
"detype": "^1.1.2",
|
|
14
15
|
"rehype-pretty-code": "0.13.0",
|
|
15
16
|
"remark-gfm": "4.0.0",
|
|
16
17
|
"shiki": "1.2.0",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
export type ContentMap = {
|
|
4
|
+
/**
|
|
5
|
+
* @returns key
|
|
6
|
+
*/
|
|
7
|
+
add(title: string, sourceLength: number, content: string): string
|
|
8
|
+
get(key: string): string | undefined
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const createContentMap = (): ContentMap => {
|
|
12
|
+
const map = new Map<string, string>()
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
add(title, length, content) {
|
|
16
|
+
const key = generateKey(`${title}_${length}`)
|
|
17
|
+
if (!map.has(key)) {
|
|
18
|
+
map.set(key, content)
|
|
19
|
+
}
|
|
20
|
+
return key
|
|
21
|
+
},
|
|
22
|
+
get(key) {
|
|
23
|
+
const val = map.get(key)
|
|
24
|
+
return val
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const generateKey = (value: string) => {
|
|
30
|
+
const hash = createHash('md5').update(value).digest('hex')
|
|
31
|
+
return `#_#_${hash}_#_#`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const contentMapKeyRE = /#_#_[0-9a-fA-F]{32}_#_#/g
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export { useSelectedLanguage }
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react'
|
|
4
|
+
|
|
5
|
+
const key = 'docpress:selectedLang'
|
|
6
|
+
const defaultSsrLang = 'ts'
|
|
7
|
+
const defaultClientLang = 'js'
|
|
8
|
+
|
|
9
|
+
declare global {
|
|
10
|
+
interface WindowEventMap {
|
|
11
|
+
'lang-storage': CustomEvent
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function useSelectedLanguage() {
|
|
16
|
+
const [selectedLang, setSelectedLang] = useState(defaultSsrLang)
|
|
17
|
+
|
|
18
|
+
const getValue = () => {
|
|
19
|
+
try {
|
|
20
|
+
return localStorage.getItem(key) ?? defaultClientLang
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.warn('Error reading from localStorage:', error)
|
|
23
|
+
return defaultClientLang
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const setValue = useCallback((value: string) => {
|
|
28
|
+
try {
|
|
29
|
+
window.localStorage.setItem(key, value)
|
|
30
|
+
setSelectedLang(value)
|
|
31
|
+
window.dispatchEvent(new CustomEvent('lang-storage'))
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.warn('Error setting localStorage:', error)
|
|
34
|
+
}
|
|
35
|
+
}, [])
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
// Initial load from localStorage
|
|
39
|
+
setSelectedLang(getValue())
|
|
40
|
+
// Update language in current tab
|
|
41
|
+
const handleCustomEvent = () => {
|
|
42
|
+
setSelectedLang(getValue())
|
|
43
|
+
}
|
|
44
|
+
// Update language if changed in another tab
|
|
45
|
+
const handleNativeStorage = (event: StorageEvent) => {
|
|
46
|
+
if (event.key === key) {
|
|
47
|
+
setSelectedLang(getValue())
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
window.addEventListener('lang-storage', handleCustomEvent)
|
|
52
|
+
window.addEventListener('storage', handleNativeStorage)
|
|
53
|
+
|
|
54
|
+
return () => {
|
|
55
|
+
window.removeEventListener('lang-storage', handleCustomEvent)
|
|
56
|
+
window.removeEventListener('storage', handleNativeStorage)
|
|
57
|
+
}
|
|
58
|
+
}, [])
|
|
59
|
+
|
|
60
|
+
return [selectedLang, setValue] as const
|
|
61
|
+
}
|
package/vite.config.ts
CHANGED
|
@@ -4,6 +4,7 @@ import mdx from '@mdx-js/rollup'
|
|
|
4
4
|
import react from '@vitejs/plugin-react-swc'
|
|
5
5
|
import type { PluginOption, UserConfig } from 'vite'
|
|
6
6
|
import { parsePageSections } from './parsePageSections.js'
|
|
7
|
+
import { detypePlugin } from './detypePlugin.js'
|
|
7
8
|
import rehypePrettyCode from 'rehype-pretty-code'
|
|
8
9
|
import remarkGfm from 'remark-gfm'
|
|
9
10
|
import { transformerNotationDiff } from '@shikijs/transformers'
|
|
@@ -17,6 +18,7 @@ const config: UserConfig = {
|
|
|
17
18
|
root,
|
|
18
19
|
plugins: [
|
|
19
20
|
parsePageSections(),
|
|
21
|
+
detypePlugin(),
|
|
20
22
|
mdx({ rehypePlugins, remarkPlugins }) as PluginOption,
|
|
21
23
|
// @vitejs/plugin-react-swc needs to be added *after* the mdx plugins
|
|
22
24
|
react(),
|