@md-plugins/vite-md-plugin 0.1.0-alpha.1
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/LICENSE.md +21 -0
- package/README.md +148 -0
- package/dist/index.d.mts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.mjs +338 -0
- package/package.json +60 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present, Jeff Galbraith
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# @md-plugins/viteMdPlugin
|
|
2
|
+
|
|
3
|
+
An **opinionated Vite plugin** that transforms Markdown files into Vue Single File Components (SFCs). This plugin integrates Markdown processing directly into your Vite-based Vue project, enabling seamless Markdown-to-Vue workflows.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Markdown to Vue SFC Transformation**: Converts Markdown files into Vue Single File Components, enabling dynamic content rendering.
|
|
8
|
+
- **Navigation Menu Integration**: Supports generating a navigation structure based on your Markdown files.
|
|
9
|
+
- **Configurable Path Prefix**: Allows setting a base path for routing or file resolution.
|
|
10
|
+
- **Opinionated and Minimal**: Focuses on simplicity, leveraging the power of Vue and Markdown for content-driven applications.
|
|
11
|
+
|
|
12
|
+
## md-plugins Used
|
|
13
|
+
|
|
14
|
+
The `viteMdPlugin` is built on top of the following plugins:
|
|
15
|
+
|
|
16
|
+
| Plugin | Description | Readme |
|
|
17
|
+
| ----------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------- |
|
|
18
|
+
| `@md-plugins/md-plugin-imports` | Extracts and processes `<script import>` blocks from Markdown. | [README](packages/md-plugin-imports/README.md) |
|
|
19
|
+
| `@md-plugins/md-plugin-codeblocks` | Enhances code block rendering with syntax highlighting, tabs, and more. | [README](packages/md-plugin-codeblocks/README.md) |
|
|
20
|
+
| `@md-plugins/md-plugin-blockquote` | Adds customizable CSS classes to blockquotes. | [README](packages/md-plugin-blockquote/README.md) |
|
|
21
|
+
| `@md-plugins/md-plugin-headers` | Extracts and processes headers for generating ToCs or managing headers. | [README](packages/md-plugin-headers/README.md) |
|
|
22
|
+
| `@md-plugins/md-plugin-inlinecode` | Adds a custom class to inline code blocks for styling. | [README](packages/md-plugin-inlinecode/README.md) |
|
|
23
|
+
| `@md-plugins/md-plugin-link` | Converts Markdown links into Vue components for SPA-friendly routing. | [README](packages/md-plugin-link/README.md) |
|
|
24
|
+
| `@md-plugins/md-plugin-table` | Adds custom classes and attributes to Markdown tables. | [README](packages/md-plugin-table/README.md) |
|
|
25
|
+
| `@md-plugins/md-plugin-title` | Extracts the first header in Markdown as the page title. | [README](packages/md-plugin-title/README.md) |
|
|
26
|
+
| `@md-plugins/md-plugin-frontmatter` | Extracts and processes frontmatter content from Markdown files. | [README](packages/md-plugin-frontmatter/README.md) |
|
|
27
|
+
| `@md-plugins/md-plugin-containers` | Adds custom containers for callouts, warnings, and more. | [README](packages/md-plugin-containers/README.md) |
|
|
28
|
+
| `@md-plugins/shared` | Shared utilities and types for the plugins. | [README](packages/shared/README.md) |
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Install the plugin via your preferred package manager:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# With npm:
|
|
36
|
+
npm install @md-plugins/vite-md-plugin
|
|
37
|
+
# Or with Yarn:
|
|
38
|
+
yarn add @md-plugins/vite-md-plugin
|
|
39
|
+
# Or with pnpm:
|
|
40
|
+
pnpm add @md-plugins/vite-md-plugin
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### Basic Setup with Vite
|
|
46
|
+
|
|
47
|
+
To use the `viteMdPlugin`, configure it in your Vite project:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
import { defineConfig } from 'vite';
|
|
51
|
+
import vue from '@vitejs/plugin-vue';
|
|
52
|
+
import { viteMdPlugin } from 'vite-md-plugin';
|
|
53
|
+
|
|
54
|
+
const menu = []; // Define your navigation menu structure here
|
|
55
|
+
const basePath = '/docs'; // Base path prefix
|
|
56
|
+
|
|
57
|
+
export default defineConfig({
|
|
58
|
+
plugins: [vue(), viteMdPlugin(basePath, menu)],
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quasar Framework Configuration
|
|
63
|
+
|
|
64
|
+
If you’re using the Quasar Framework, additional configuration is needed to enable support for `.md` files:
|
|
65
|
+
|
|
66
|
+
1. Update `quasar.config.(js|ts)`:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import { viteMdPlugin } from '@md-plugins/vite-md-plugin';
|
|
70
|
+
import { menu } from './src/assets/menu'; // be sure to create this file
|
|
71
|
+
|
|
72
|
+
export default defineConfig((ctx) => {
|
|
73
|
+
// ...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
build: {
|
|
78
|
+
vueRouterMode: 'history', // Required for proper hash link handling
|
|
79
|
+
|
|
80
|
+
viteVuePluginOptions: {
|
|
81
|
+
include: [/\.(vue|md)$/], // Include Markdown files
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
vitePlugins: [
|
|
85
|
+
viteMdPlugin(ctx.appPaths.srcDir + '/pages', menu),
|
|
86
|
+
// ...
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
framework: {
|
|
91
|
+
autoImportVueExtensions: ['vue', 'md'], // Enable auto-import for Markdown extensions
|
|
92
|
+
},
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
2. Ensure that your routes and hash links are compatible with Vue Router's history mode.
|
|
96
|
+
|
|
97
|
+
## Navigation Menu Integration
|
|
98
|
+
|
|
99
|
+
The `viteMdPlugin` allows you to define a navigation structure that can be updated dynamically based on the Markdown files in your project:
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const menu = [
|
|
103
|
+
{ title: 'Home', path: '/home' },
|
|
104
|
+
{ title: 'About', path: '/about' },
|
|
105
|
+
];
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This menu is passed as a parameter to the plugin and can be used to build a dynamic sidebar or navigation bar in your application.
|
|
109
|
+
|
|
110
|
+
## Options
|
|
111
|
+
|
|
112
|
+
The `viteMdPlugin` accepts the following parameters:
|
|
113
|
+
|
|
114
|
+
| Parameter | Type | Description |
|
|
115
|
+
| --------- | ---------- | ------------------------------------------------------------------------------------------ |
|
|
116
|
+
| path | string | The base path prefix for routing or file resolution. |
|
|
117
|
+
| menu | MenuItem[] | An array representing the navigation menu structure. Each item should have title and path. |
|
|
118
|
+
|
|
119
|
+
## MenuItem Type
|
|
120
|
+
|
|
121
|
+
The `menu` parameter should conform to the following structure:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
export interface MenuItem {
|
|
125
|
+
name: string;
|
|
126
|
+
path?: string;
|
|
127
|
+
icon?: string;
|
|
128
|
+
iconColor?: string;
|
|
129
|
+
rightIcon?: string;
|
|
130
|
+
rightIconColor?: string;
|
|
131
|
+
badge?: string;
|
|
132
|
+
children?: MenuItem[];
|
|
133
|
+
external?: boolean;
|
|
134
|
+
expanded?: boolean;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Testing
|
|
139
|
+
|
|
140
|
+
To run the tests for this plugin, use the following command:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pnpm test
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for details.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
interface MenuItem {
|
|
2
|
+
name: string;
|
|
3
|
+
path?: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
iconColor?: string;
|
|
6
|
+
rightIcon?: string;
|
|
7
|
+
rightIconColor?: string;
|
|
8
|
+
badge?: string;
|
|
9
|
+
children?: MenuItem[];
|
|
10
|
+
external?: boolean;
|
|
11
|
+
expanded?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface MenuNode {
|
|
14
|
+
name: string;
|
|
15
|
+
path?: string;
|
|
16
|
+
external?: boolean;
|
|
17
|
+
children?: MenuNode[];
|
|
18
|
+
}
|
|
19
|
+
interface FlatMenuEntry {
|
|
20
|
+
name: string;
|
|
21
|
+
category: string | null;
|
|
22
|
+
path: string;
|
|
23
|
+
prev?: FlatMenuEntry;
|
|
24
|
+
next?: FlatMenuEntry;
|
|
25
|
+
}
|
|
26
|
+
type FlatMenu = Record<string, FlatMenuEntry>;
|
|
27
|
+
interface NavItem extends FlatMenuEntry {
|
|
28
|
+
classes: string;
|
|
29
|
+
}
|
|
30
|
+
interface RelatedItem {
|
|
31
|
+
name: string;
|
|
32
|
+
category: string;
|
|
33
|
+
path: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Transforms markdown content into Vue Single File Component (SFC) format.
|
|
38
|
+
*
|
|
39
|
+
* @param code - The markdown content to be transformed.
|
|
40
|
+
* @param id - The identifier (typically the file path) of the markdown file.
|
|
41
|
+
* @returns The transformed Vue SFC content, or null if the file is not a markdown file.
|
|
42
|
+
* @throws Will throw an error if the markdown transformation process fails.
|
|
43
|
+
*/
|
|
44
|
+
declare function transform(code: string, id: string): {
|
|
45
|
+
code: string;
|
|
46
|
+
map: null;
|
|
47
|
+
} | null;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a Vite plugin for processing Markdown files.
|
|
50
|
+
* This plugin transforms Markdown content into Vue Single File Components (SFCs).
|
|
51
|
+
*
|
|
52
|
+
* @param path - The base path prefix to be used for routing or file resolution.
|
|
53
|
+
* @param menu - An array of MenuItem objects representing the navigation menu structure.
|
|
54
|
+
* @returns A Vite plugin object with pre-configured settings for Markdown processing.
|
|
55
|
+
*/
|
|
56
|
+
declare function viteMdPlugin(path: string, menu: MenuItem[]): {
|
|
57
|
+
name: string;
|
|
58
|
+
enforce: string;
|
|
59
|
+
transform: typeof transform;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export { type FlatMenu, type FlatMenuEntry, type MenuItem, type MenuNode, type NavItem, type RelatedItem, viteMdPlugin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
interface MenuItem {
|
|
2
|
+
name: string;
|
|
3
|
+
path?: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
iconColor?: string;
|
|
6
|
+
rightIcon?: string;
|
|
7
|
+
rightIconColor?: string;
|
|
8
|
+
badge?: string;
|
|
9
|
+
children?: MenuItem[];
|
|
10
|
+
external?: boolean;
|
|
11
|
+
expanded?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface MenuNode {
|
|
14
|
+
name: string;
|
|
15
|
+
path?: string;
|
|
16
|
+
external?: boolean;
|
|
17
|
+
children?: MenuNode[];
|
|
18
|
+
}
|
|
19
|
+
interface FlatMenuEntry {
|
|
20
|
+
name: string;
|
|
21
|
+
category: string | null;
|
|
22
|
+
path: string;
|
|
23
|
+
prev?: FlatMenuEntry;
|
|
24
|
+
next?: FlatMenuEntry;
|
|
25
|
+
}
|
|
26
|
+
type FlatMenu = Record<string, FlatMenuEntry>;
|
|
27
|
+
interface NavItem extends FlatMenuEntry {
|
|
28
|
+
classes: string;
|
|
29
|
+
}
|
|
30
|
+
interface RelatedItem {
|
|
31
|
+
name: string;
|
|
32
|
+
category: string;
|
|
33
|
+
path: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Transforms markdown content into Vue Single File Component (SFC) format.
|
|
38
|
+
*
|
|
39
|
+
* @param code - The markdown content to be transformed.
|
|
40
|
+
* @param id - The identifier (typically the file path) of the markdown file.
|
|
41
|
+
* @returns The transformed Vue SFC content, or null if the file is not a markdown file.
|
|
42
|
+
* @throws Will throw an error if the markdown transformation process fails.
|
|
43
|
+
*/
|
|
44
|
+
declare function transform(code: string, id: string): {
|
|
45
|
+
code: string;
|
|
46
|
+
map: null;
|
|
47
|
+
} | null;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a Vite plugin for processing Markdown files.
|
|
50
|
+
* This plugin transforms Markdown content into Vue Single File Components (SFCs).
|
|
51
|
+
*
|
|
52
|
+
* @param path - The base path prefix to be used for routing or file resolution.
|
|
53
|
+
* @param menu - An array of MenuItem objects representing the navigation menu structure.
|
|
54
|
+
* @returns A Vite plugin object with pre-configured settings for Markdown processing.
|
|
55
|
+
*/
|
|
56
|
+
declare function viteMdPlugin(path: string, menu: MenuItem[]): {
|
|
57
|
+
name: string;
|
|
58
|
+
enforce: string;
|
|
59
|
+
transform: typeof transform;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export { type FlatMenu, type FlatMenuEntry, type MenuItem, type MenuNode, type NavItem, type RelatedItem, viteMdPlugin };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import MarkdownIt from 'markdown-it';
|
|
2
|
+
import { frontmatterPlugin } from '@md-plugins/md-plugin-frontmatter';
|
|
3
|
+
import { importsPlugin } from '@md-plugins/md-plugin-imports';
|
|
4
|
+
import { headersPlugin } from '@md-plugins/md-plugin-headers';
|
|
5
|
+
import { linkPlugin } from '@md-plugins/md-plugin-link';
|
|
6
|
+
import { inlinecodePlugin } from '@md-plugins/md-plugin-inlinecode';
|
|
7
|
+
import { imagePlugin } from '@md-plugins/md-plugin-image';
|
|
8
|
+
import { codeblocksPlugin } from '@md-plugins/md-plugin-codeblocks';
|
|
9
|
+
import { blockquotePlugin } from '@md-plugins/md-plugin-blockquote';
|
|
10
|
+
import { tablePlugin } from '@md-plugins/md-plugin-table';
|
|
11
|
+
import { titlePlugin } from '@md-plugins/md-plugin-title';
|
|
12
|
+
import { containersPlugin } from '@md-plugins/md-plugin-containers';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
|
|
15
|
+
const createContainer = (container, containerType, defaultTitle) => {
|
|
16
|
+
const containerTypeLen = containerType.length;
|
|
17
|
+
return [
|
|
18
|
+
container,
|
|
19
|
+
containerType,
|
|
20
|
+
{
|
|
21
|
+
render(tokens, idx) {
|
|
22
|
+
const token = tokens[idx];
|
|
23
|
+
if (!token) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
const title = token.info.trim().slice(containerTypeLen).trim() || defaultTitle;
|
|
27
|
+
if (containerType === "details") {
|
|
28
|
+
return token.nesting === 1 ? `<details class="markdown-note markdown-note--${containerType}"><summary class="markdown-note__title">${title}</summary>
|
|
29
|
+
` : "</details>\n";
|
|
30
|
+
}
|
|
31
|
+
return token.nesting === 1 ? `<div class="markdown-note markdown-note--${containerType}"><p class="markdown-note__title">${title}</p>
|
|
32
|
+
` : "</div>\n";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
};
|
|
37
|
+
function createMarkdownRenderer(options = {}) {
|
|
38
|
+
const md2 = new MarkdownIt({
|
|
39
|
+
html: true,
|
|
40
|
+
linkify: true,
|
|
41
|
+
typographer: true,
|
|
42
|
+
...options,
|
|
43
|
+
breaks: true
|
|
44
|
+
});
|
|
45
|
+
md2.use(frontmatterPlugin);
|
|
46
|
+
md2.use(importsPlugin);
|
|
47
|
+
md2.use(titlePlugin);
|
|
48
|
+
md2.use(headersPlugin, { level: [2, 3] });
|
|
49
|
+
const containers = [
|
|
50
|
+
{ type: "tip", defaultTitle: "TIP" },
|
|
51
|
+
{ type: "warning", defaultTitle: "WARNING" },
|
|
52
|
+
{ type: "danger", defaultTitle: "WARNING" },
|
|
53
|
+
{ type: "details", defaultTitle: "Details" }
|
|
54
|
+
];
|
|
55
|
+
md2.use(containersPlugin, containers, createContainer);
|
|
56
|
+
md2.use(blockquotePlugin, { blockquoteClass: "markdown-note" });
|
|
57
|
+
md2.use(tablePlugin, {
|
|
58
|
+
tableClass: "markdown-page-table",
|
|
59
|
+
tableHeaderClass: "text-left",
|
|
60
|
+
tableToken: "q-markup-table",
|
|
61
|
+
tableAttributes: [
|
|
62
|
+
[":wrap-cells", "true"],
|
|
63
|
+
[":flat", "true"],
|
|
64
|
+
[":bordered", "true"]
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
md2.use(codeblocksPlugin, {
|
|
68
|
+
containerComponent: "MarkdownPrerender",
|
|
69
|
+
copyButtonComponent: "MarkdownCopyButton",
|
|
70
|
+
pageScripts: [
|
|
71
|
+
"import MarkdownPrerender from 'components/md/MarkdownPrerender'",
|
|
72
|
+
"import MarkdownCopyButton from 'components/md/MarkdownCopyButton.vue'"
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
md2.use(linkPlugin);
|
|
76
|
+
md2.use(inlinecodePlugin);
|
|
77
|
+
md2.use(imagePlugin);
|
|
78
|
+
return {
|
|
79
|
+
// env: Environment for storing metadata
|
|
80
|
+
render(code, env = {}) {
|
|
81
|
+
const html = md2.render(code, env);
|
|
82
|
+
return {
|
|
83
|
+
html,
|
|
84
|
+
frontmatter: env.frontmatter || {},
|
|
85
|
+
// comes from md_plugin_frontmatter
|
|
86
|
+
title: env.title || "",
|
|
87
|
+
// comes from md_plugin_title
|
|
88
|
+
env
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const md = createMarkdownRenderer();
|
|
94
|
+
|
|
95
|
+
let prev = null;
|
|
96
|
+
function menuWalk(prefix, menuNodes, node, path, parentName) {
|
|
97
|
+
const newPath = path + (node.path ? `/${node.path}` : "");
|
|
98
|
+
if (node.children) {
|
|
99
|
+
node.children.forEach((childNode) => {
|
|
100
|
+
menuWalk(prefix, menuNodes, childNode, newPath, node.name);
|
|
101
|
+
});
|
|
102
|
+
} else if (!node.external) {
|
|
103
|
+
const current = {
|
|
104
|
+
name: node.name,
|
|
105
|
+
category: parentName,
|
|
106
|
+
path: newPath
|
|
107
|
+
};
|
|
108
|
+
if (prev) {
|
|
109
|
+
prev.next = {
|
|
110
|
+
name: current.name,
|
|
111
|
+
category: current.category,
|
|
112
|
+
path: current.path
|
|
113
|
+
};
|
|
114
|
+
current.prev = {
|
|
115
|
+
name: prev.name,
|
|
116
|
+
category: prev.category,
|
|
117
|
+
path: prev.path
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
menuNodes[join(prefix, newPath + ".md")] = current;
|
|
121
|
+
if (node.path) {
|
|
122
|
+
menuNodes[join(prefix, newPath + "/" + node.path + ".md")] = current;
|
|
123
|
+
}
|
|
124
|
+
prev = current;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function generateFlatMenu(prefix, menu) {
|
|
128
|
+
const menuNodes = {};
|
|
129
|
+
prev = null;
|
|
130
|
+
if (menu) {
|
|
131
|
+
menu.forEach((node) => {
|
|
132
|
+
menuWalk(prefix, menuNodes, node, "", null);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return menuNodes;
|
|
136
|
+
}
|
|
137
|
+
function convertToRelated(prefix, menuNodes, entry, id) {
|
|
138
|
+
const menuEntry = menuNodes[join(prefix, entry + ".md")];
|
|
139
|
+
if (!menuEntry) {
|
|
140
|
+
console.error("[flat-menu] ERROR - wrong related link:", entry, "@id", id);
|
|
141
|
+
return {
|
|
142
|
+
name: "",
|
|
143
|
+
category: "",
|
|
144
|
+
path: ""
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
name: menuEntry.name,
|
|
149
|
+
category: menuEntry.category,
|
|
150
|
+
path: menuEntry.path
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function splitRenderedContent(mdPageContent) {
|
|
155
|
+
const scriptRE = /<script import>\n((.|\n)*?)\n<\/script>/g;
|
|
156
|
+
const userScripts = /* @__PURE__ */ new Set();
|
|
157
|
+
const mdContent = mdPageContent.replace(scriptRE, (_, p1) => {
|
|
158
|
+
userScripts.add(p1);
|
|
159
|
+
return "";
|
|
160
|
+
});
|
|
161
|
+
return { mdContent, userScripts };
|
|
162
|
+
}
|
|
163
|
+
function createNav(id, env, flatMenu) {
|
|
164
|
+
if (flatMenu) {
|
|
165
|
+
const menuItem = flatMenu[id];
|
|
166
|
+
const nav = [];
|
|
167
|
+
if (menuItem !== void 0) {
|
|
168
|
+
const { prev, next } = menuItem;
|
|
169
|
+
if (prev !== void 0) {
|
|
170
|
+
nav.push({ ...prev, classes: "markdown-page__related--left" });
|
|
171
|
+
}
|
|
172
|
+
if (next !== void 0) {
|
|
173
|
+
nav.push({ ...next, classes: "markdown-page__related--right" });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (nav.length > 0) {
|
|
177
|
+
env.frontmatter.nav = nav;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function parseToc(toc) {
|
|
182
|
+
let wasHeader = true;
|
|
183
|
+
let headerIndex = 1;
|
|
184
|
+
let subheaderIndex;
|
|
185
|
+
const list = toc.map((entry) => {
|
|
186
|
+
if (entry.sub === true) {
|
|
187
|
+
if (wasHeader === true) {
|
|
188
|
+
subheaderIndex = 1;
|
|
189
|
+
} else {
|
|
190
|
+
subheaderIndex++;
|
|
191
|
+
}
|
|
192
|
+
wasHeader = false;
|
|
193
|
+
} else {
|
|
194
|
+
wasHeader = true;
|
|
195
|
+
headerIndex++;
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
...entry,
|
|
199
|
+
title: entry.sub === true ? `${headerIndex}.${subheaderIndex}. ${entry.title}` : `${headerIndex}. ${entry.title}`
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
return JSON.stringify(list);
|
|
203
|
+
}
|
|
204
|
+
function getVueComponent(rendered, code, id, prefix, menu) {
|
|
205
|
+
let flatMenu;
|
|
206
|
+
if (menu) {
|
|
207
|
+
flatMenu = generateFlatMenu(prefix, menu);
|
|
208
|
+
createNav(id, rendered.env, flatMenu);
|
|
209
|
+
}
|
|
210
|
+
const frontmatter = rendered?.frontmatter || {};
|
|
211
|
+
if (frontmatter.editLink !== false) {
|
|
212
|
+
frontmatter.editLink = id.substring(
|
|
213
|
+
id.indexOf("src/pages/") + 10,
|
|
214
|
+
id.length - 3
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const title = frontmatter.title || rendered.env.title || rendered.title || "Generic Page";
|
|
218
|
+
const desc = frontmatter.desc || false;
|
|
219
|
+
const overline = frontmatter.overline || false;
|
|
220
|
+
const heading = rendered.env.heading || !!title || false;
|
|
221
|
+
const related = frontmatter.related && Array.isArray(frontmatter.related) && frontmatter.related.map(
|
|
222
|
+
(entry) => convertToRelated(prefix, flatMenu, entry, id)
|
|
223
|
+
) || false;
|
|
224
|
+
const nav = frontmatter.nav || false;
|
|
225
|
+
const badge = frontmatter.badge || false;
|
|
226
|
+
const toc = rendered.env.toc || false;
|
|
227
|
+
const editLink = frontmatter.editLink || false;
|
|
228
|
+
frontmatter.components || false;
|
|
229
|
+
const scope = frontmatter.scope || false;
|
|
230
|
+
const examples = frontmatter.examples || false;
|
|
231
|
+
const { mdContent, userScripts } = splitRenderedContent(rendered.html);
|
|
232
|
+
const pageScripts = [
|
|
233
|
+
...Array.from(rendered.env.pageScripts || []),
|
|
234
|
+
...Array.from(userScripts || [])
|
|
235
|
+
].join("\n");
|
|
236
|
+
return `<template>
|
|
237
|
+
<markdown-page
|
|
238
|
+
title="${title}"
|
|
239
|
+
${desc !== false ? `desc="${desc}"` : ""}
|
|
240
|
+
${overline !== false ? `overline="${overline}"` : ""}
|
|
241
|
+
${badge !== false ? `badge="${badge}"` : ""}
|
|
242
|
+
${heading !== false ? "heading" : ""}
|
|
243
|
+
${editLink !== false ? `edit-link="${editLink}"` : ""}
|
|
244
|
+
${toc !== false ? ':toc="toc"' : ""}
|
|
245
|
+
${related !== false ? ':related="related"' : ""}
|
|
246
|
+
${nav !== false ? ':nav="nav"' : ""}>${mdContent}</markdown-page>
|
|
247
|
+
</template>
|
|
248
|
+
<script setup>
|
|
249
|
+
import { copyHeading } from 'components/md/markdown-utils'
|
|
250
|
+
${examples !== false ? `
|
|
251
|
+
import { provide } from 'vue'
|
|
252
|
+
provide('_markdown_examples_', process.env.CLIENT
|
|
253
|
+
? { name: '${examples}', list: import('examples:${examples}') }
|
|
254
|
+
: { name: '${examples}' })
|
|
255
|
+
` : ""}
|
|
256
|
+
${related !== false ? `const related = ${JSON.stringify(related)}` : ""}
|
|
257
|
+
${nav !== false ? `const nav = ${JSON.stringify(nav)}` : ""}
|
|
258
|
+
${toc !== false ? `const toc = ${parseToc(toc)}` : ""}
|
|
259
|
+
${scope !== false ? `const scope = ${JSON.stringify(scope)}` : ""}
|
|
260
|
+
${pageScripts}
|
|
261
|
+
<\/script>`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const markdownLinkRE = /<MarkdownLink /;
|
|
265
|
+
const markdownApiRE = /<MarkdownApi /;
|
|
266
|
+
const markdownInstallationRE = /<MarkdownInstallation /;
|
|
267
|
+
const markdownTreeRE = /<MarkdownTree /;
|
|
268
|
+
function mdParse(code, id, prefix, menu) {
|
|
269
|
+
const env = {
|
|
270
|
+
frontmatter: {
|
|
271
|
+
id
|
|
272
|
+
},
|
|
273
|
+
pageScripts: /* @__PURE__ */ new Set()
|
|
274
|
+
};
|
|
275
|
+
env.pageScripts.add(
|
|
276
|
+
"import MarkdownPage from 'src/layouts/MarkdownPage.vue'"
|
|
277
|
+
);
|
|
278
|
+
if (markdownApiRE.test(code) === true) {
|
|
279
|
+
env.pageScripts.add(
|
|
280
|
+
"import MarkdownApi from 'components/md/MarkdownApi.vue'"
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (markdownInstallationRE.test(code) === true) {
|
|
284
|
+
env.pageScripts.add(
|
|
285
|
+
"import MarkdownInstallation from 'components/md/MarkdownInstallation.vue'"
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
if (markdownTreeRE.test(code) === true) {
|
|
289
|
+
env.pageScripts.add(
|
|
290
|
+
"import MarkdownTree from 'components/md/MarkdownTree.vue'"
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
const results = md.render(code, env);
|
|
294
|
+
if (env.frontmatter.examples !== void 0) {
|
|
295
|
+
env.pageScripts.add(
|
|
296
|
+
"import MarkdownExample from 'components/md/MarkdownExample.vue'"
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
if (markdownLinkRE.test(code) === true) {
|
|
300
|
+
env.pageScripts.add(
|
|
301
|
+
"import MarkdownLink from 'components/md/MarkdownLink.vue'"
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
const component = getVueComponent(results, code, id, prefix, menu);
|
|
305
|
+
return {
|
|
306
|
+
code: component,
|
|
307
|
+
map: null
|
|
308
|
+
// No source map provided
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const mdRE = /\.md$/;
|
|
313
|
+
let globalMenu = [];
|
|
314
|
+
let globalPrefix = "";
|
|
315
|
+
function transform(code, id) {
|
|
316
|
+
if (!mdRE.test(id)) return null;
|
|
317
|
+
try {
|
|
318
|
+
return mdParse(code, id, globalPrefix, globalMenu);
|
|
319
|
+
} catch (err) {
|
|
320
|
+
console.error(`Error processing Markdown file: ${id}`, err);
|
|
321
|
+
throw new Error(
|
|
322
|
+
`Markdown transform failed: ${err instanceof Error ? err.message : String(err)}`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const mdPlugins = {
|
|
327
|
+
name: "md-plugins:vitePlugin",
|
|
328
|
+
enforce: "pre",
|
|
329
|
+
// before vue
|
|
330
|
+
transform
|
|
331
|
+
};
|
|
332
|
+
function viteMdPlugin(path, menu) {
|
|
333
|
+
globalMenu = menu;
|
|
334
|
+
globalPrefix = path;
|
|
335
|
+
return mdPlugins;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export { viteMdPlugin };
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@md-plugins/vite-md-plugin",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "A very opinionated Vite plugin for @md-plugins.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"markdown-it",
|
|
7
|
+
"quasarframework",
|
|
8
|
+
"vue",
|
|
9
|
+
"utils",
|
|
10
|
+
"vite",
|
|
11
|
+
"vite-plugin"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/md-plugins",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/md-plugins/md-plugins/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/md-plugins/md-plugins.git"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "hawkeye64 <galbraith64@gmail.com>",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"import": {
|
|
27
|
+
"types": "./dist/index.d.mts",
|
|
28
|
+
"default": "./dist/index.mjs"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"module": "./dist/index.mjs",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"files": [
|
|
35
|
+
"./dist"
|
|
36
|
+
],
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/markdown-it": "^14.1.2",
|
|
39
|
+
"markdown-it": "^14.1.0",
|
|
40
|
+
"@md-plugins/shared": "0.1.0-alpha.1",
|
|
41
|
+
"@md-plugins/md-plugin-containers": "0.1.0-alpha.1",
|
|
42
|
+
"@md-plugins/md-plugin-headers": "0.1.0-alpha.1",
|
|
43
|
+
"@md-plugins/md-plugin-frontmatter": "0.1.0-alpha.1",
|
|
44
|
+
"@md-plugins/md-plugin-inlinecode": "0.1.0-alpha.1",
|
|
45
|
+
"@md-plugins/md-plugin-imports": "0.1.0-alpha.1",
|
|
46
|
+
"@md-plugins/md-plugin-table": "0.1.0-alpha.1",
|
|
47
|
+
"@md-plugins/md-plugin-title": "0.1.0-alpha.1",
|
|
48
|
+
"@md-plugins/md-plugin-codeblocks": "0.1.0-alpha.1",
|
|
49
|
+
"@md-plugins/md-plugin-blockquote": "0.1.0-alpha.1",
|
|
50
|
+
"@md-plugins/md-plugin-image": "0.1.0-alpha.1",
|
|
51
|
+
"@md-plugins/md-plugin-link": "0.1.0-alpha.1"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "unbuild",
|
|
58
|
+
"clean": "rm -rf dist"
|
|
59
|
+
}
|
|
60
|
+
}
|