reallysimpledocs 1.0.0-beta.4 → 1.0.0
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/README.md +59 -39
- package/package.json +11 -11
- package/src/astro/index.js +64 -98
- package/src/astro/preview-scripts.js +140 -0
- package/src/astro/preview-source.js +150 -0
- package/src/astro/rehype-basecoat-tables.js +62 -0
- package/src/components/index.js +1 -0
- package/src/css/custom.css +197 -58
- package/src/css/styles/luma.css +9 -0
- package/src/css/styles/lyra.css +9 -0
- package/src/css/styles/maia.css +9 -0
- package/src/css/styles/mira.css +9 -0
- package/src/css/styles/nova.css +9 -0
- package/src/css/styles/rhea.css +9 -0
- package/src/css/styles/sera.css +9 -0
- package/src/css/styles/vega.css +9 -0
- package/src/runtime/components/BaseLayout.astro +22 -28
- package/src/runtime/components/CommandDialog.astro +52 -35
- package/src/runtime/components/DefaultContentHeader.astro +0 -17
- package/src/runtime/components/DefaultHead.astro +110 -0
- package/src/runtime/components/DefaultHeadContent.astro +2 -0
- package/src/runtime/components/DefaultSidebarHeader.astro +1 -1
- package/src/runtime/components/DocsLayout.astro +99 -166
- package/src/runtime/components/DropdownMenu.astro +1 -1
- package/src/runtime/components/ThemeToggle.astro +21 -0
- package/src/runtime/components/mdx/Callout.astro +2 -32
- package/src/runtime/components/mdx/Card.astro +3 -8
- package/src/runtime/components/mdx/CardGrid.astro +1 -1
- package/src/runtime/components/mdx/Code.astro +21 -27
- package/src/runtime/components/mdx/CodeGroup.astro +114 -24
- package/src/runtime/components/mdx/Preview.astro +7 -13
- package/src/runtime/components/mdx/Steps.astro +1 -5
- package/src/runtime/components/mdx/Tab.astro +6 -0
- package/src/runtime/components/mdx/Tabs.astro +7 -7
- package/src/runtime/lib/code-meta.js +8 -0
- package/src/runtime/lib/docs.js +141 -119
- package/src/runtime/lib/highlight.js +1 -14
- package/src/runtime/lib/html.js +9 -0
- package/src/runtime/lib/macros.js +10 -5
- package/src/runtime/lib/markdown-export.js +240 -169
- package/src/runtime/pages/doc.astro +1 -3
- package/src/runtime/pages/llms-full.txt.ts +2 -2
- package/src/runtime/pages/llms.txt.ts +2 -2
- package/src/runtime/pages/markdown.ts +2 -2
- package/src/runtime/pages/search-index.ts +12 -3
- package/CHANGELOG.md +0 -35
- package/src/css/overrides.css +0 -1
- package/src/css/sources.css +0 -1
- package/src/runtime/lib/markdown-exporters/index.js +0 -144
- package/src/runtime/lib/mdx-source.js +0 -19
- package/src/runtime/lib/preview-source-plugin.js +0 -33
- package/src/runtime/scripts/copy-code.js +0 -130
- package/src/site/ContentHeader.astro +0 -33
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# ReallySimpleDocs
|
|
2
2
|
|
|
3
|
-
A simple documentation system for Astro, built with
|
|
3
|
+
A simple documentation system for Astro, built with Basecoat and Tailwind CSS.
|
|
4
4
|
|
|
5
|
-
ReallySimpleDocs
|
|
5
|
+
ReallySimpleDocs keeps docs portable: content lives in `docs/`, navigation lives in `docs/docs.json`, and the Astro integration owns the docs UI, search, Markdown exports, and LLM files.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Install
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Add ReallySimpleDocs to an Astro site:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm install reallysimpledocs
|
|
12
|
+
npm install reallysimpledocs basecoat-css@beta tailwindcss
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Configure the integration:
|
|
16
16
|
|
|
17
17
|
```js
|
|
18
18
|
// astro.config.mjs
|
|
@@ -25,17 +25,16 @@ export default defineConfig({
|
|
|
25
25
|
docsDir: "./docs",
|
|
26
26
|
routeBase: "/docs",
|
|
27
27
|
style: "vega",
|
|
28
|
-
customCss: ["./src/docs.css"],
|
|
29
28
|
site: {
|
|
30
|
-
title: "
|
|
31
|
-
description: "
|
|
29
|
+
title: "Acme Docs",
|
|
30
|
+
description: "Documentation for Acme.",
|
|
32
31
|
},
|
|
33
32
|
}),
|
|
34
33
|
],
|
|
35
34
|
});
|
|
36
35
|
```
|
|
37
36
|
|
|
38
|
-
Create docs:
|
|
37
|
+
Create your first docs page:
|
|
39
38
|
|
|
40
39
|
```text
|
|
41
40
|
docs/
|
|
@@ -61,68 +60,89 @@ docs/
|
|
|
61
60
|
Welcome to the docs.
|
|
62
61
|
```
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
## What it provides
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
- Static docs routes from Markdown and MDX files.
|
|
66
|
+
- Sidebar navigation from `docs/docs.json`.
|
|
67
|
+
- Lunr search with a built-in command dialog.
|
|
68
|
+
- Syntax-highlighted code blocks with copy buttons.
|
|
69
|
+
- `llms.txt`, `llms-full.txt`, and per-page Markdown exports.
|
|
70
|
+
- Basecoat styling with style-specific RSD CSS for docs surfaces.
|
|
71
|
+
- Component slots for additive head content, sidebar chrome, and header controls.
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
# Button
|
|
73
|
+
## Customization
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
```
|
|
75
|
+
Use `components.ContentHeader` for controls between search and the built-in theme toggle:
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
```js
|
|
78
|
+
reallySimpleDocs({
|
|
79
|
+
components: {
|
|
80
|
+
ContentHeader: "./src/components/ContentHeader.astro",
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
```
|
|
75
84
|
|
|
76
|
-
|
|
85
|
+
Use `components.Head` to add tags to `<head>`. RSD's built-in metadata, theme setup, Basecoat JavaScript, copy-code behavior, and search behavior still render.
|
|
77
86
|
|
|
78
|
-
|
|
87
|
+
Use `customCss` for project CSS imported after Basecoat and RSD styles:
|
|
79
88
|
|
|
80
|
-
```
|
|
81
|
-
|
|
89
|
+
```js
|
|
90
|
+
reallySimpleDocs({
|
|
91
|
+
style: "nova",
|
|
92
|
+
customCss: ["./src/docs.css"],
|
|
93
|
+
});
|
|
82
94
|
```
|
|
83
95
|
|
|
84
|
-
|
|
96
|
+
Use `css: false` only when you provide the full Tailwind/Basecoat/RSD stylesheet pipeline yourself. RSD exports its own CSS for that case:
|
|
85
97
|
|
|
86
98
|
```css
|
|
87
|
-
|
|
88
|
-
@import "
|
|
89
|
-
@import "basecoat-css/base";
|
|
90
|
-
@import "basecoat-css/styles/vega";
|
|
91
|
-
|
|
92
|
-
@source "../src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}";
|
|
99
|
+
@import "reallysimpledocs/css";
|
|
100
|
+
@import "reallysimpledocs/css/styles/vega";
|
|
93
101
|
```
|
|
94
102
|
|
|
95
|
-
|
|
103
|
+
## Search outside docs
|
|
104
|
+
|
|
105
|
+
Render the exported `CommandDialog` on any Astro page where you want docs search:
|
|
96
106
|
|
|
97
107
|
```astro
|
|
98
108
|
---
|
|
99
|
-
import "
|
|
100
|
-
import "basecoat-css/all.min";
|
|
109
|
+
import { CommandDialog } from "reallysimpledocs/components";
|
|
101
110
|
---
|
|
102
111
|
|
|
103
|
-
<
|
|
104
|
-
```
|
|
112
|
+
<CommandDialog />
|
|
105
113
|
|
|
106
|
-
|
|
114
|
+
<button type="button" class="btn" onclick="window.rsdOpenCommandSearch?.()">
|
|
115
|
+
Search docs
|
|
116
|
+
</button>
|
|
117
|
+
```
|
|
107
118
|
|
|
108
|
-
## Local development
|
|
119
|
+
## Local development
|
|
109
120
|
|
|
110
121
|
```bash
|
|
111
122
|
npm install
|
|
112
123
|
npm run dev
|
|
113
124
|
```
|
|
114
125
|
|
|
126
|
+
Useful checks before publishing:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm run build
|
|
130
|
+
npm pack --dry-run
|
|
131
|
+
npm run smoke:consumer
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`npm run check` runs all three.
|
|
135
|
+
|
|
115
136
|
## Documentation
|
|
116
137
|
|
|
117
|
-
Go to [
|
|
138
|
+
Go to [reallysimpledocs.com](https://reallysimpledocs.com).
|
|
118
139
|
|
|
119
|
-
## Support
|
|
140
|
+
## Support
|
|
120
141
|
|
|
121
|
-
- [Contribute code](https://github.com/hunvreus/reallysimpledocs/blob/main/CONTRIBUTING.md)
|
|
122
142
|
- [Report issues](https://github.com/hunvreus/reallysimpledocs/issues)
|
|
123
143
|
- [Sponsor me](https://github.com/sponsors/hunvreus)
|
|
124
144
|
- [Star the project on GitHub](https://github.com/hunvreus/reallysimpledocs)
|
|
125
145
|
|
|
126
146
|
## License
|
|
127
147
|
|
|
128
|
-
[MIT](
|
|
148
|
+
[MIT](LICENSE.md)
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reallysimpledocs",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.js",
|
|
7
7
|
"./astro": "./src/astro/index.js",
|
|
8
|
-
"./components": "./src/components/index.js"
|
|
8
|
+
"./components": "./src/components/index.js",
|
|
9
|
+
"./css": "./src/css/custom.css",
|
|
10
|
+
"./css/styles/*": "./src/css/styles/*.css",
|
|
11
|
+
"./css/styles/*.css": "./src/css/styles/*.css"
|
|
9
12
|
},
|
|
10
13
|
"files": [
|
|
11
14
|
"src",
|
|
12
15
|
"README.md",
|
|
13
|
-
"CHANGELOG.md",
|
|
14
16
|
"LICENSE.md"
|
|
15
17
|
],
|
|
16
18
|
"scripts": {
|
|
@@ -24,25 +26,23 @@
|
|
|
24
26
|
"@astrojs/markdown-remark": "^7.2.0",
|
|
25
27
|
"@astrojs/mdx": "^6.0.3",
|
|
26
28
|
"@tailwindcss/vite": "4.1.17",
|
|
27
|
-
"astro": "^6.4.6",
|
|
28
29
|
"github-slugger": "^2.0.0",
|
|
29
30
|
"lucide-static": "^0.552.0",
|
|
30
31
|
"lunr": "^2.3.9",
|
|
31
32
|
"marked": "^16.4.1",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"remark-gfm": "^4.0.1",
|
|
35
|
-
"remark-mdx": "^3.1.1",
|
|
36
|
-
"remark-parse": "^11.0.0",
|
|
33
|
+
"rehype-parse": "^9.0.1",
|
|
34
|
+
"rehype-stringify": "^10.0.1",
|
|
37
35
|
"shiki": "^4.1.0",
|
|
38
36
|
"unified": "^11.0.5"
|
|
39
37
|
},
|
|
40
38
|
"peerDependencies": {
|
|
41
|
-
"
|
|
39
|
+
"astro": "^6.4.6",
|
|
40
|
+
"basecoat-css": "^1.0.0-beta.6",
|
|
42
41
|
"tailwindcss": ">=4.1.17 <5"
|
|
43
42
|
},
|
|
44
43
|
"devDependencies": {
|
|
45
|
-
"
|
|
44
|
+
"astro": "^6.4.6",
|
|
45
|
+
"basecoat-css": "^1.0.0-beta.6",
|
|
46
46
|
"tailwindcss": "4.1.17"
|
|
47
47
|
},
|
|
48
48
|
"overrides": {
|
package/src/astro/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { unified as astroMarkdown } from "@astrojs/markdown-remark";
|
|
5
4
|
import mdx from "@astrojs/mdx";
|
|
5
|
+
import { unified } from "@astrojs/markdown-remark";
|
|
6
6
|
import tailwindcss from "@tailwindcss/vite";
|
|
7
|
-
import {
|
|
7
|
+
import { rehypeBasecoatTables } from "./rehype-basecoat-tables.js";
|
|
8
|
+
import { remarkPreviewSource } from "./preview-source.js";
|
|
9
|
+
import { transformPreviewScripts } from "./preview-scripts.js";
|
|
8
10
|
|
|
9
11
|
const basecoatStyles = new Set(["vega", "nova", "maia", "lyra", "mira", "luma", "sera", "rhea"]);
|
|
10
12
|
|
|
@@ -28,12 +30,7 @@ const normalizeArray = (value) => (Array.isArray(value) ? value : value ? [value
|
|
|
28
30
|
|
|
29
31
|
const normalizeImportPath = (filePath) => filePath.replaceAll(path.sep, "/");
|
|
30
32
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const isInsideDir = (dir, file) => {
|
|
34
|
-
const relative = path.relative(dir, file);
|
|
35
|
-
return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
36
|
-
};
|
|
33
|
+
const normalizeBoolean = (value, fallback = true) => (value === undefined ? fallback : Boolean(value));
|
|
37
34
|
|
|
38
35
|
const shikiConfig = {
|
|
39
36
|
themes: {
|
|
@@ -63,18 +60,6 @@ function collectMdxModules(docsDir) {
|
|
|
63
60
|
return modules;
|
|
64
61
|
}
|
|
65
62
|
|
|
66
|
-
function componentModuleSource(modules) {
|
|
67
|
-
const entries = Object.entries(modules);
|
|
68
|
-
const imports = entries
|
|
69
|
-
.map(([name, moduleId], index) => `import component${index} from ${JSON.stringify(moduleId)};`)
|
|
70
|
-
.join("\n");
|
|
71
|
-
return `${imports}
|
|
72
|
-
export default {
|
|
73
|
-
${entries.map(([name], index) => ` ${JSON.stringify(name)}: component${index},`).join("\n")}
|
|
74
|
-
};
|
|
75
|
-
`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
63
|
export default function reallySimpleDocs(options = {}) {
|
|
79
64
|
const style = options.style || "vega";
|
|
80
65
|
if (!basecoatStyles.has(style)) {
|
|
@@ -87,9 +72,9 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
87
72
|
siteFile: options.siteFile || null,
|
|
88
73
|
style,
|
|
89
74
|
customCss: normalizeArray(options.customCss),
|
|
75
|
+
css: normalizeBoolean(options.css),
|
|
76
|
+
bodyAttrs: options.bodyAttrs || {},
|
|
90
77
|
components: options.components || {},
|
|
91
|
-
mdxComponents: normalizeRecord(options.mdxComponents),
|
|
92
|
-
markdownSerializers: normalizeRecord(options.markdownSerializers),
|
|
93
78
|
routeBase: options.routeBase ?? "/docs",
|
|
94
79
|
assetsBase: options.assetsBase || "/assets",
|
|
95
80
|
};
|
|
@@ -97,57 +82,51 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
97
82
|
return {
|
|
98
83
|
name: "reallysimpledocs",
|
|
99
84
|
hooks: {
|
|
100
|
-
"astro:config:setup": ({ injectRoute, updateConfig, config }) => {
|
|
85
|
+
"astro:config:setup": ({ injectRoute, injectScript, updateConfig, config }) => {
|
|
101
86
|
const root = fileURLToPath(config.root);
|
|
102
87
|
const generatedDir = path.join(root, ".astro", "reallysimpledocs");
|
|
103
88
|
const generatedStyles = path.join(generatedDir, "styles.css");
|
|
104
89
|
const docsDir = path.resolve(root, normalizedOptions.docsDir);
|
|
105
|
-
const resolveUserId = (id) => (id?.startsWith(".") ? path.resolve(root, id) : id);
|
|
106
|
-
const markdownSerializers = Object.fromEntries(
|
|
107
|
-
Object.entries(normalizedOptions.markdownSerializers).map(([name, moduleId]) => [
|
|
108
|
-
name,
|
|
109
|
-
normalizeImportPath(resolveUserId(moduleId)),
|
|
110
|
-
]),
|
|
111
|
-
);
|
|
112
|
-
const mdxComponents = Object.fromEntries(
|
|
113
|
-
Object.entries(normalizedOptions.mdxComponents).map(([name, moduleId]) => [
|
|
114
|
-
name,
|
|
115
|
-
normalizeImportPath(resolveUserId(moduleId)),
|
|
116
|
-
]),
|
|
117
|
-
);
|
|
118
90
|
const virtualConfig = {
|
|
119
91
|
...normalizedOptions,
|
|
120
92
|
root,
|
|
121
93
|
docsDir,
|
|
122
94
|
siteFile: normalizedOptions.siteFile ? path.resolve(root, normalizedOptions.siteFile) : null,
|
|
123
|
-
mdxComponents,
|
|
124
|
-
markdownSerializers,
|
|
125
95
|
routeBase: normalizeRouteBase(normalizedOptions.routeBase),
|
|
126
96
|
};
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
SidebarFooter:
|
|
133
|
-
|
|
97
|
+
const mdxModules = collectMdxModules(docsDir);
|
|
98
|
+
const resolveUserId = (id) => (id?.startsWith(".") ? path.resolve(root, id) : id);
|
|
99
|
+
const componentDefaults = {
|
|
100
|
+
Head: runtimePath("components/DefaultHeadContent.astro"),
|
|
101
|
+
SidebarHeader: runtimePath("components/DefaultSidebarHeader.astro"),
|
|
102
|
+
SidebarFooter: runtimePath("components/DefaultSidebarFooter.astro"),
|
|
103
|
+
ContentHeader: runtimePath("components/DefaultContentHeader.astro"),
|
|
134
104
|
};
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
'@import "basecoat-css/base";',
|
|
141
|
-
`@import "basecoat-css/styles/${style}";`,
|
|
142
|
-
`@import ${JSON.stringify(cssPath("sources.css"))};`,
|
|
143
|
-
`@import ${JSON.stringify(cssPath("custom.css"))};`,
|
|
144
|
-
`@import ${JSON.stringify(cssPath("overrides.css"))};`,
|
|
145
|
-
...normalizedOptions.customCss.map((css) => `@import ${JSON.stringify(resolveUserId(css))};`),
|
|
146
|
-
`@source ${JSON.stringify(runtimePath("**/*.{astro,js,ts}"))};`,
|
|
147
|
-
`@source ${JSON.stringify(path.join(virtualConfig.docsDir, "**/*.{md,mdx}"))};`,
|
|
148
|
-
"",
|
|
149
|
-
].join("\n"),
|
|
105
|
+
const components = Object.fromEntries(
|
|
106
|
+
Object.entries(componentDefaults).map(([name, defaultPath]) => [
|
|
107
|
+
name,
|
|
108
|
+
normalizedOptions.components[name] || defaultPath,
|
|
109
|
+
]),
|
|
150
110
|
);
|
|
111
|
+
fs.mkdirSync(generatedDir, { recursive: true });
|
|
112
|
+
const styleSource = normalizedOptions.css
|
|
113
|
+
? [
|
|
114
|
+
'@import "tailwindcss";',
|
|
115
|
+
'@import "basecoat-css/base";',
|
|
116
|
+
`@import "basecoat-css/styles/${style}";`,
|
|
117
|
+
`@import ${JSON.stringify(cssPath("custom.css"))};`,
|
|
118
|
+
`@import ${JSON.stringify(cssPath(`styles/${style}.css`))};`,
|
|
119
|
+
...normalizedOptions.customCss.map((css) => `@import ${JSON.stringify(resolveUserId(css))};`),
|
|
120
|
+
`@source ${JSON.stringify(path.join(root, "src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"))};`,
|
|
121
|
+
`@source ${JSON.stringify(runtimePath("**/*.{astro,js,ts}"))};`,
|
|
122
|
+
`@source ${JSON.stringify(path.join(virtualConfig.docsDir, "**/*.{md,mdx}"))};`,
|
|
123
|
+
"",
|
|
124
|
+
].join("\n")
|
|
125
|
+
: "/* ReallySimpleDocs CSS disabled by config. */\n";
|
|
126
|
+
fs.writeFileSync(generatedStyles, styleSource);
|
|
127
|
+
if (normalizedOptions.css) {
|
|
128
|
+
injectScript("page-ssr", 'import "virtual:reallysimpledocs/styles.css";');
|
|
129
|
+
}
|
|
151
130
|
|
|
152
131
|
injectRoute({
|
|
153
132
|
pattern: routePattern(normalizedOptions.routeBase),
|
|
@@ -178,8 +157,9 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
178
157
|
}),
|
|
179
158
|
],
|
|
180
159
|
markdown: {
|
|
181
|
-
processor:
|
|
182
|
-
remarkPlugins: [
|
|
160
|
+
processor: unified({
|
|
161
|
+
remarkPlugins: [[remarkPreviewSource, { docsDir }]],
|
|
162
|
+
rehypePlugins: [rehypeBasecoatTables],
|
|
183
163
|
}),
|
|
184
164
|
shikiConfig,
|
|
185
165
|
},
|
|
@@ -190,42 +170,30 @@ export default function reallySimpleDocs(options = {}) {
|
|
|
190
170
|
plugins: [
|
|
191
171
|
tailwindcss(),
|
|
192
172
|
{
|
|
193
|
-
name: "reallysimpledocs-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
server.watcher.add(path.join(docsDir, "**/*.mdx"));
|
|
202
|
-
server.watcher.on("add", (file) => {
|
|
203
|
-
if (file.endsWith(".mdx") && isInsideDir(docsDir, file)) invalidateMdxPages();
|
|
204
|
-
});
|
|
205
|
-
server.watcher.on("unlink", (file) => {
|
|
206
|
-
if (file.endsWith(".mdx") && isInsideDir(docsDir, file)) invalidateMdxPages();
|
|
207
|
-
});
|
|
173
|
+
name: "reallysimpledocs-preview-scripts",
|
|
174
|
+
enforce: "pre",
|
|
175
|
+
transform(code, id) {
|
|
176
|
+
if (!id.endsWith(".mdx") || !isInsideDir(id, docsDir)) return null;
|
|
177
|
+
const transformed = transformPreviewScripts(code);
|
|
178
|
+
return transformed === code ? null : { code: transformed, map: null };
|
|
208
179
|
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "reallysimpledocs-virtual-modules",
|
|
209
183
|
resolveId(id) {
|
|
210
184
|
if (id === "virtual:reallysimpledocs/config") return "\0virtual:reallysimpledocs/config";
|
|
211
185
|
if (id === "virtual:reallysimpledocs/mdx-pages") return "\0virtual:reallysimpledocs/mdx-pages";
|
|
212
|
-
if (id === "virtual:reallysimpledocs/mdx-components") return "\0virtual:reallysimpledocs/mdx-components";
|
|
213
186
|
if (id === "virtual:reallysimpledocs/styles.css") return generatedStyles;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return "\0virtual:reallysimpledocs/components/SidebarFooter";
|
|
219
|
-
}
|
|
220
|
-
if (id === "virtual:reallysimpledocs/components/ContentHeader") {
|
|
221
|
-
return "\0virtual:reallysimpledocs/components/ContentHeader";
|
|
187
|
+
for (const name of Object.keys(components)) {
|
|
188
|
+
if (id === `virtual:reallysimpledocs/components/${name}`) {
|
|
189
|
+
return `\0virtual:reallysimpledocs/components/${name}`;
|
|
190
|
+
}
|
|
222
191
|
}
|
|
223
192
|
return null;
|
|
224
193
|
},
|
|
225
194
|
load(id) {
|
|
226
195
|
if (id === "\0virtual:reallysimpledocs/config") return `export default ${JSON.stringify(virtualConfig)};`;
|
|
227
196
|
if (id === "\0virtual:reallysimpledocs/mdx-pages") {
|
|
228
|
-
const mdxModules = collectMdxModules(docsDir);
|
|
229
197
|
const imports = Object.entries(mdxModules)
|
|
230
198
|
.map(([slug, filePath], index) => `import * as mdx${index} from ${JSON.stringify(filePath)};`)
|
|
231
199
|
.join("\n");
|
|
@@ -241,17 +209,10 @@ export async function getMdxPage(slug) {
|
|
|
241
209
|
}
|
|
242
210
|
`;
|
|
243
211
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return `export { default } from ${JSON.stringify(resolveUserId(components.SidebarHeader))};`;
|
|
249
|
-
}
|
|
250
|
-
if (id === "\0virtual:reallysimpledocs/components/SidebarFooter") {
|
|
251
|
-
return `export { default } from ${JSON.stringify(resolveUserId(components.SidebarFooter))};`;
|
|
252
|
-
}
|
|
253
|
-
if (id === "\0virtual:reallysimpledocs/components/ContentHeader") {
|
|
254
|
-
return `export { default } from ${JSON.stringify(resolveUserId(components.ContentHeader))};`;
|
|
212
|
+
for (const [name, componentPath] of Object.entries(components)) {
|
|
213
|
+
if (id === `\0virtual:reallysimpledocs/components/${name}`) {
|
|
214
|
+
return `export { default } from ${JSON.stringify(resolveUserId(componentPath))};`;
|
|
215
|
+
}
|
|
255
216
|
}
|
|
256
217
|
return null;
|
|
257
218
|
},
|
|
@@ -263,3 +224,8 @@ export async function getMdxPage(slug) {
|
|
|
263
224
|
},
|
|
264
225
|
};
|
|
265
226
|
}
|
|
227
|
+
|
|
228
|
+
function isInsideDir(filePath, dir) {
|
|
229
|
+
const relative = path.relative(dir, filePath);
|
|
230
|
+
return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
231
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const previewTag = "Preview";
|
|
2
|
+
const scriptTagPattern = /<script(?<attrs>[^>]*)>(?<content>[\s\S]*?)<\/script>/g;
|
|
3
|
+
|
|
4
|
+
export function transformPreviewScripts(source) {
|
|
5
|
+
let output = "";
|
|
6
|
+
let cursor = 0;
|
|
7
|
+
|
|
8
|
+
while (cursor < source.length) {
|
|
9
|
+
const open = findNextTag(source, previewTag, cursor);
|
|
10
|
+
if (!open) {
|
|
11
|
+
output += source.slice(cursor);
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const close = findMatchingCloseTag(source, open);
|
|
16
|
+
if (!close) {
|
|
17
|
+
output += source.slice(cursor);
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const body = source.slice(open.end, close.start);
|
|
22
|
+
const rewrittenBody = transformPreviewScriptBody(body);
|
|
23
|
+
|
|
24
|
+
output += source.slice(cursor, open.start);
|
|
25
|
+
if (rewrittenBody === body) {
|
|
26
|
+
output += source.slice(open.start, close.end);
|
|
27
|
+
} else {
|
|
28
|
+
output += addPreviewCodeAttribute(source.slice(open.start, open.end), body);
|
|
29
|
+
output += rewrittenBody;
|
|
30
|
+
output += source.slice(close.start, close.end);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
cursor = close.end;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return output;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function transformPreviewScriptBody(body) {
|
|
40
|
+
return body.replace(scriptTagPattern, (match, attrs = "", content = "") => {
|
|
41
|
+
if (/\ssrc\s*=/.test(attrs) || /(?:^|\s)is:inline(?:\s|=|$)/.test(attrs)) return match;
|
|
42
|
+
return `<script${attrs} is:inline>{${JSON.stringify(content)}}</script>`;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function addPreviewCodeAttribute(openTag, body) {
|
|
47
|
+
if (/(?:^|\s)code(?:\s|=|>)/.test(openTag)) return openTag;
|
|
48
|
+
return `${openTag.slice(0, -1)} code={${JSON.stringify(body.trim())}}>`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function findMatchingCloseTag(source, open) {
|
|
52
|
+
if (open.selfClosing) return null;
|
|
53
|
+
let depth = 1;
|
|
54
|
+
let cursor = open.end;
|
|
55
|
+
|
|
56
|
+
while (cursor < source.length) {
|
|
57
|
+
const tag = findNextTag(source, previewTag, cursor);
|
|
58
|
+
if (!tag) return null;
|
|
59
|
+
if (tag.closing) depth -= 1;
|
|
60
|
+
else if (!tag.selfClosing) depth += 1;
|
|
61
|
+
if (depth === 0) return tag;
|
|
62
|
+
cursor = tag.end;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function findNextTag(source, name, fromIndex) {
|
|
69
|
+
let cursor = fromIndex;
|
|
70
|
+
while (cursor < source.length) {
|
|
71
|
+
const start = source.indexOf("<", cursor);
|
|
72
|
+
if (start === -1) return null;
|
|
73
|
+
if (source.startsWith("<!--", start)) {
|
|
74
|
+
const commentEnd = source.indexOf("-->", start + 4);
|
|
75
|
+
cursor = commentEnd === -1 ? source.length : commentEnd + 3;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const tag = readTag(source, start);
|
|
80
|
+
if (!tag) {
|
|
81
|
+
cursor = start + 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (tag.name === name) return tag;
|
|
85
|
+
if (!tag.closing && !tag.selfClosing && tag.name.toLowerCase() === "script") {
|
|
86
|
+
const scriptEnd = source.toLowerCase().indexOf("</script>", tag.end);
|
|
87
|
+
cursor = scriptEnd === -1 ? source.length : scriptEnd + "</script>".length;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
cursor = tag.end;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function readTag(source, start) {
|
|
96
|
+
let cursor = start + 1;
|
|
97
|
+
let closing = false;
|
|
98
|
+
|
|
99
|
+
if (source[cursor] === "/") {
|
|
100
|
+
closing = true;
|
|
101
|
+
cursor += 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const nameStart = cursor;
|
|
105
|
+
while (/[$A-Z_a-z0-9-]/.test(source[cursor] || "")) cursor += 1;
|
|
106
|
+
const name = source.slice(nameStart, cursor);
|
|
107
|
+
if (!name) return null;
|
|
108
|
+
|
|
109
|
+
let quote = "";
|
|
110
|
+
while (cursor < source.length) {
|
|
111
|
+
const char = source[cursor];
|
|
112
|
+
if (quote) {
|
|
113
|
+
if (char === "\\" && source[cursor + 1]) {
|
|
114
|
+
cursor += 2;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (char === quote) quote = "";
|
|
118
|
+
cursor += 1;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
122
|
+
quote = char;
|
|
123
|
+
cursor += 1;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (char === ">") {
|
|
127
|
+
const beforeEnd = source.slice(start, cursor).trimEnd();
|
|
128
|
+
return {
|
|
129
|
+
start,
|
|
130
|
+
end: cursor + 1,
|
|
131
|
+
name,
|
|
132
|
+
closing,
|
|
133
|
+
selfClosing: beforeEnd.endsWith("/"),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
cursor += 1;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return null;
|
|
140
|
+
}
|