chore: 升级 monorepo 架构
This commit is contained in:
40
package/document/src/plugin/common-plugins.ts
Normal file
40
package/document/src/plugin/common-plugins.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import Markdown from "vite-plugin-md";
|
||||
import container from "markdown-it-container";
|
||||
import highlight from "./highlight";
|
||||
import snippet from "./snippet";
|
||||
import demo from "./demo";
|
||||
import createTitle from "./create-title";
|
||||
import createQuote from "./create-quote";
|
||||
import createDescribe from "./create-describe";
|
||||
import createTable from "./create-table";
|
||||
import createAnchor from "./create-anchor";
|
||||
import preWrapper from "./pre-wrapper";
|
||||
import previousNext from "./previous-next";
|
||||
|
||||
const plugins = [
|
||||
vue({
|
||||
include: [/\.vue$/, /\.md$/],
|
||||
}),
|
||||
Markdown({
|
||||
markdownItOptions: {
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
highlight,
|
||||
},
|
||||
markdownItSetup(md) {
|
||||
md.use(snippet)
|
||||
.use(preWrapper)
|
||||
.use(container, "demo", demo)
|
||||
.use(...createTable("table", ""))
|
||||
.use(...createQuote("quote", ""))
|
||||
.use(...createTitle("title", ""))
|
||||
.use(...createDescribe("describe", ""))
|
||||
.use(...createAnchor("anchor", ""))
|
||||
.use(...previousNext("previousNext", ""));
|
||||
},
|
||||
}),
|
||||
] as any;
|
||||
|
||||
export default plugins;
|
||||
36
package/document/src/plugin/create-anchor.ts
Normal file
36
package/document/src/plugin/create-anchor.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import container from "markdown-it-container";
|
||||
import type Token from "markdown-it/lib/token";
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{ render(tokens: Token[], idx: number): string }
|
||||
];
|
||||
|
||||
export default function createContainer(
|
||||
klass: string,
|
||||
defaultTitle: string
|
||||
): ContainerArgs {
|
||||
return [
|
||||
container,
|
||||
klass,
|
||||
{
|
||||
render(tokens, idx) {
|
||||
const token = tokens[idx];
|
||||
if (token.nesting === 1) {
|
||||
const anchors = [];
|
||||
for (const token of tokens) {
|
||||
if (token.nesting === 1 && token.info.includes("title")) {
|
||||
const info = token.info.trim().slice(klass.length).trim();
|
||||
anchors.push(info);
|
||||
}
|
||||
}
|
||||
// 此处仅支持 string | number | boolean 类型
|
||||
return `<lay-anchor anchors="${anchors}" :currIndex="-1" :show="true">`;
|
||||
} else {
|
||||
return "</lay-anchor>\n";
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
29
package/document/src/plugin/create-describe.ts
Normal file
29
package/document/src/plugin/create-describe.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import container from "markdown-it-container";
|
||||
import type Token from "markdown-it/lib/token";
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{ render(tokens: Token[], idx: number): string }
|
||||
];
|
||||
|
||||
export default function createContainer(
|
||||
klass: string,
|
||||
defaultTitle: string
|
||||
): ContainerArgs {
|
||||
return [
|
||||
container,
|
||||
klass,
|
||||
{
|
||||
render(tokens, idx) {
|
||||
const token = tokens[idx];
|
||||
const info = token.info.trim().slice(klass.length).trim();
|
||||
if (token.nesting === 1) {
|
||||
return `<p style="padding-left: 30px;padding-top: 20px;padding-bottom: 20px;">${info}`;
|
||||
} else {
|
||||
return "</p>\n";
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
29
package/document/src/plugin/create-quote.ts
Normal file
29
package/document/src/plugin/create-quote.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import container from "markdown-it-container";
|
||||
import type Token from "markdown-it/lib/token";
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{ render(tokens: Token[], idx: number): string }
|
||||
];
|
||||
|
||||
export default function createContainer(
|
||||
klass: string,
|
||||
defaultTitle: string
|
||||
): ContainerArgs {
|
||||
return [
|
||||
container,
|
||||
klass,
|
||||
{
|
||||
render(tokens, idx) {
|
||||
const token = tokens[idx];
|
||||
const info = token.info.trim().slice(klass.length).trim();
|
||||
if (token.nesting === 1) {
|
||||
return `<lay-quote style="margin-left:0px;margin-right:0px;margin-top:20px;margin-bottom:40px;">${info}`;
|
||||
} else {
|
||||
return "</lay-quote>\n";
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
29
package/document/src/plugin/create-table.ts
Normal file
29
package/document/src/plugin/create-table.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import container from "markdown-it-container";
|
||||
import type Token from "markdown-it/lib/token";
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{ render(tokens: Token[], idx: number): string }
|
||||
];
|
||||
|
||||
export default function createContainer(
|
||||
klass: string,
|
||||
defaultTitle: string
|
||||
): ContainerArgs {
|
||||
return [
|
||||
container,
|
||||
klass,
|
||||
{
|
||||
render(tokens, idx) {
|
||||
const token = tokens[idx];
|
||||
const info = token.info.trim().slice(klass.length).trim();
|
||||
if (token.nesting === 1) {
|
||||
return `<lay-table-box>${info}`;
|
||||
} else {
|
||||
return "</lay-table-box>\n";
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
31
package/document/src/plugin/create-title.ts
Normal file
31
package/document/src/plugin/create-title.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import container from "markdown-it-container";
|
||||
import type Token from "markdown-it/lib/token";
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{ render(tokens: Token[], idx: number): string }
|
||||
];
|
||||
|
||||
export default function createContainer(
|
||||
klass: string,
|
||||
defaultTitle: string
|
||||
): ContainerArgs {
|
||||
return [
|
||||
container,
|
||||
klass,
|
||||
{
|
||||
render(tokens, idx) {
|
||||
const token = tokens[idx];
|
||||
const info = token.info.trim().slice(klass.length).trim();
|
||||
if (token.nesting === 1) {
|
||||
return `<lay-field id="${info || defaultTitle}" title="${
|
||||
info || defaultTitle
|
||||
}" style="margin-top:21px;margin-bottom: 20px;">`;
|
||||
} else {
|
||||
return "</lay-field>\n";
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
141
package/document/src/plugin/demo.ts
Normal file
141
package/document/src/plugin/demo.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import markdown from "markdown-it";
|
||||
import highlight from "./highlight";
|
||||
import type Token from "markdown-it/lib/token";
|
||||
|
||||
function assignScript(script: string) {
|
||||
const dependencies = {} as Record<string, string[]>;
|
||||
const attrs = {} as Record<string, string>;
|
||||
const content = script
|
||||
.replace(/import\s?\{.*\}.*/g, (item) => {
|
||||
const key = getInnerString(item.replace(/'/g, '"'), '"', '"');
|
||||
const value = getInnerString(item.replace(/\s+/g, ""), "{", "}");
|
||||
const list = value ? value.split(",") : [];
|
||||
if (key && dependencies[key]) {
|
||||
dependencies[key] = dependencies[key].concat(list);
|
||||
} else if (key) {
|
||||
dependencies[key] = list;
|
||||
}
|
||||
return "";
|
||||
})
|
||||
/**
|
||||
* const -> let
|
||||
*
|
||||
* const a = -> let a =
|
||||
* const a = -> a =
|
||||
*/
|
||||
.replace(/(const|let|var)\s\w*\s?=/g, (item) => {
|
||||
const attr = getInnerString(item, "\\s", "\\s?=");
|
||||
if (attr && !(attr in attrs)) {
|
||||
attrs[attr] = attr;
|
||||
return `let ${attr} =`;
|
||||
} else {
|
||||
return attr + " =";
|
||||
}
|
||||
})
|
||||
// Remove extra line breaks
|
||||
.replace(/\n+/gm, "\n");
|
||||
// Combine the import
|
||||
const reImport = Object.keys(dependencies).reduce((all, item) => {
|
||||
const filterAttrs = [...new Set(dependencies[item])];
|
||||
return all + `import {${filterAttrs + ","}} from '${item}';\n`;
|
||||
}, "");
|
||||
|
||||
return reImport + content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract part of the new string from the middle of the string
|
||||
* @param {string} string string
|
||||
* @param {string} prefix RegExp string
|
||||
* @param {string} postfix RegExp string
|
||||
* @param {string} type g | m | i
|
||||
*/
|
||||
function getInnerString(
|
||||
string: string,
|
||||
prefix: string,
|
||||
postfix = "",
|
||||
type: "i" | "g" | "m" = "i"
|
||||
): string | undefined {
|
||||
const result = new RegExp(`${prefix}(.*)${postfix}`, type);
|
||||
const match = string.match(result);
|
||||
return match ? match[1].trim() : undefined;
|
||||
}
|
||||
|
||||
let script = ""; // Record the <script> label of the current page
|
||||
|
||||
export default {
|
||||
render: (tokens: Token[], idx: number): string => {
|
||||
// the `demo` block of the current page
|
||||
const htmlBlock = tokens.filter((item) => item.type === "html_block");
|
||||
const { nesting, info = "", map } = tokens[idx];
|
||||
|
||||
if (nesting === -1) {
|
||||
return "</lay-code>";
|
||||
}
|
||||
|
||||
const matchedInfo = info.trim().match(/^demo\s+(.*)$/);
|
||||
const description = matchedInfo && matchedInfo[1];
|
||||
const descTemplate = markdown().render(description || "");
|
||||
let str = ""; // copy the current `demo` block code
|
||||
let lastLine = NaN;
|
||||
|
||||
for (let i = 0; i < htmlBlock.length; i++) {
|
||||
const item = htmlBlock[i];
|
||||
|
||||
if (item.map && map && item.map[0] >= map[0] && item.map[1] <= map[1]) {
|
||||
const { map, content } = item;
|
||||
const delta = map[0] - (lastLine || map[1]);
|
||||
|
||||
if (delta > 0) {
|
||||
str += "\n".repeat(delta);
|
||||
}
|
||||
str += content;
|
||||
lastLine = map[1];
|
||||
if (i === 0) {
|
||||
script = "";
|
||||
}
|
||||
// Remove top <template>
|
||||
if (/^<template>/.test(content)) {
|
||||
const reContent = content.match(/^<template>((\s|\S)*)<\/template>/m);
|
||||
|
||||
htmlBlock[i].content = (reContent && reContent[1]) || "";
|
||||
}
|
||||
// Extract the <script> label content
|
||||
if (content.includes("<script")) {
|
||||
if (/export\sdefault\s?\{/m.test(content)) {
|
||||
const setup = content.match(
|
||||
/setup\s?\(\)\s?\{((\s|\S)*)return\s?\{/m
|
||||
);
|
||||
const reContent = content.replace(
|
||||
/export\sdefault\s?\{((\s|\S)*)\}/m,
|
||||
(setup && setup[1]) || ""
|
||||
);
|
||||
const reScript = reContent.match(
|
||||
/^<script\s?.*?>((\s|\S)*)<\/script>/m
|
||||
);
|
||||
script += (reScript && reScript[1]) || "";
|
||||
} else {
|
||||
const reScript = content.match(
|
||||
/^<script\s?.*?>((\s|\S)*)<\/script>/m
|
||||
);
|
||||
script += (reScript && reScript[1]) || "";
|
||||
}
|
||||
htmlBlock[i].content = "";
|
||||
}
|
||||
// Change the last content to <script> of the current page
|
||||
if (i + 1 === htmlBlock.length) {
|
||||
htmlBlock[i].content = `
|
||||
<script setup>
|
||||
${assignScript(script)}
|
||||
</script>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `
|
||||
<lay-code>
|
||||
${description ? `<template #description>${descTemplate}</template>` : ""}
|
||||
<template #code>${highlight(str, "vue")}</template>
|
||||
`;
|
||||
},
|
||||
};
|
||||
44
package/document/src/plugin/highlight.ts
Normal file
44
package/document/src/plugin/highlight.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import prism from "prismjs";
|
||||
import loadLanguages from "prismjs/components/index";
|
||||
import escapeHtml from "escape-html";
|
||||
|
||||
loadLanguages(["markup", "css", "javascript"]);
|
||||
|
||||
function wrap(code: string, lang: string): string {
|
||||
if (lang === "text") {
|
||||
code = escapeHtml(code);
|
||||
}
|
||||
return `<pre v-pre><code>${code}</code></pre>`;
|
||||
}
|
||||
|
||||
export default (str: string, lang: string): string => {
|
||||
if (!lang) {
|
||||
return wrap(str, "text");
|
||||
}
|
||||
lang = lang.toLowerCase();
|
||||
const rawLang = lang;
|
||||
if (lang === "vue" || lang === "html") {
|
||||
lang = "markup";
|
||||
}
|
||||
if (lang === "md") {
|
||||
lang = "markdown";
|
||||
}
|
||||
if (lang === "ts") {
|
||||
lang = "typescript";
|
||||
}
|
||||
if (lang === "py") {
|
||||
lang = "python";
|
||||
}
|
||||
if (!prism.languages[lang]) {
|
||||
try {
|
||||
loadLanguages([lang]);
|
||||
} catch (e) {
|
||||
console.warn(lang, e);
|
||||
}
|
||||
}
|
||||
if (prism.languages[lang]) {
|
||||
const code = prism.highlight(str, prism.languages[lang], lang);
|
||||
return wrap(code, rawLang);
|
||||
}
|
||||
return wrap(str, "text");
|
||||
};
|
||||
11
package/document/src/plugin/pre-wrapper.ts
Normal file
11
package/document/src/plugin/pre-wrapper.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import MarkdownIt from "markdown-it";
|
||||
|
||||
export default (md: MarkdownIt): void => {
|
||||
const fence = md.renderer.rules.fence!;
|
||||
md.renderer.rules.fence = (...args) => {
|
||||
const [tokens, idx] = args;
|
||||
const token = tokens[idx];
|
||||
const rawCode = fence(...args);
|
||||
return `<div class="language-${token.info.trim()}">${rawCode}</div>`;
|
||||
};
|
||||
};
|
||||
55
package/document/src/plugin/previous-next.ts
Normal file
55
package/document/src/plugin/previous-next.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import container from "markdown-it-container";
|
||||
import type Token from "markdown-it/lib/token";
|
||||
import menus from "../view/utils/menus";
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{
|
||||
render(tokens: Token[], idx: number): string;
|
||||
}
|
||||
];
|
||||
|
||||
export default function createContainer(
|
||||
klass: string,
|
||||
defaultTitle: string
|
||||
): ContainerArgs {
|
||||
return [
|
||||
container,
|
||||
klass,
|
||||
{
|
||||
render(tokens, idx) {
|
||||
const token = tokens[idx];
|
||||
const info = token.info.trim().slice(klass.length).trim();
|
||||
const menusChild = menus.map((item) => item.children).flat(1);
|
||||
let prevIndex = 0;
|
||||
let nextIndex = 0;
|
||||
menusChild.forEach((item, index) => {
|
||||
if (item.subTitle === info) {
|
||||
prevIndex = index - 1;
|
||||
nextIndex = index + 1;
|
||||
}
|
||||
});
|
||||
if (token.nesting === 1) {
|
||||
return `<div style="display: flex; justify-content: space-between; margin-top:20px;">
|
||||
<div><router-link to="${
|
||||
menusChild[prevIndex]?.path
|
||||
}" class="lay-link" style="display: ${
|
||||
prevIndex < 0 ? "none" : ""
|
||||
}"> <lay-icon type="layui-icon-left"/>${
|
||||
menusChild[prevIndex]?.title
|
||||
}</router-link></div>
|
||||
<div><router-link to="${
|
||||
menusChild[nextIndex]?.path
|
||||
}" class="lay-link" style="display: ${
|
||||
nextIndex >= menusChild.length ? "none" : ""
|
||||
}">${
|
||||
menusChild[nextIndex]?.title
|
||||
} <lay-icon type="layui-icon-right"/></router-link> </div>
|
||||
</div>`;
|
||||
} else {
|
||||
return ``;
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
39
package/document/src/plugin/snippet.ts
Normal file
39
package/document/src/plugin/snippet.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import fs from "fs";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { RuleBlock } from "markdown-it/lib/parser_block";
|
||||
|
||||
export default (md: MarkdownIt): void => {
|
||||
const parser: RuleBlock = (state, startLine, endLine, silent) => {
|
||||
const CH = "<".charCodeAt(0);
|
||||
const pos = state.bMarks[startLine] + state.tShift[startLine];
|
||||
const max = state.eMarks[startLine];
|
||||
if (state.sCount[startLine] - state.blkIndent >= 4) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
const ch = state.src.charCodeAt(pos + i);
|
||||
if (ch !== CH || pos + i >= max) return false;
|
||||
}
|
||||
if (silent) {
|
||||
return true;
|
||||
}
|
||||
const start = pos + 3;
|
||||
const end = state.skipSpacesBack(max, pos);
|
||||
const rawPath = state.src
|
||||
.slice(start, end)
|
||||
.trim()
|
||||
.replace(/^@/, process.cwd());
|
||||
const content = fs.existsSync(rawPath)
|
||||
? fs.readFileSync(rawPath).toString()
|
||||
: "Not found: " + rawPath;
|
||||
const meta = rawPath.replace(rawPath, "");
|
||||
state.line = startLine + 1;
|
||||
const token = state.push("fence", "code", 0);
|
||||
token.info = rawPath.split(".").pop() + meta;
|
||||
token.content = content;
|
||||
token.markup = "```";
|
||||
token.map = [startLine, startLine + 1];
|
||||
return true;
|
||||
};
|
||||
md.block.ruler.before("fence", "snippet", parser);
|
||||
};
|
||||
82
package/document/src/plugin/usePlayground.ts
Normal file
82
package/document/src/plugin/usePlayground.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
const scriptRe = /<script[^>]*>([\s\S]*)<\/script>/;
|
||||
const exportDefaultRe = /export\s*default\s*\{([\s\S]*)\}\;?\s*<\/script>/;
|
||||
const setupRe = /setup\s*\(\)\s*\{([\s\S]*)return\s*\{([\s\S]*)\}\;?\s*\}\;?/;
|
||||
const layerRe =
|
||||
/import\s?\{\s?layer\s?\}\s?from\s?[\"|\']\@layui\/layer-vue[\"|\']/;
|
||||
|
||||
// danger: 以下字符串拼接代码不可改动缩进或换行,否则会影响 URI hash 解码后代码的排版
|
||||
const MAIN_FILE_NAME = "App.vue";
|
||||
|
||||
/**
|
||||
* 将代码编码为 URI hash, 生成 playground 链接
|
||||
* @param source 源码
|
||||
* @param convertSetupSugar 转换 setup,仅字符串替换,没有做任何语法分析
|
||||
* @returns 处理后的代码,URI hsah, playground 链接
|
||||
}
|
||||
*/
|
||||
export const usePlayGround = (source: string, convertSetupSugar: boolean) => {
|
||||
const decodeCode = source;
|
||||
const scriptResult = decodeCode.match(scriptRe);
|
||||
|
||||
// 替换 script 标签
|
||||
// $1 正则第一个括号匹配的内容
|
||||
let code: string = decodeCode;
|
||||
if (convertSetupSugar) {
|
||||
if (scriptResult) {
|
||||
code = decodeCode.replace(
|
||||
scriptRe,
|
||||
`<script lang="ts" setup>$1
|
||||
</script>`
|
||||
);
|
||||
} else {
|
||||
code = `${decodeCode}
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
`;
|
||||
}
|
||||
|
||||
// 去除 export default,保留其中的内容
|
||||
const exportDefaultResult = code.match(exportDefaultRe);
|
||||
if (exportDefaultResult) {
|
||||
code = code.replace(
|
||||
exportDefaultRe,
|
||||
trimBr(exportDefaultResult[1] + `</script>`).trim()
|
||||
);
|
||||
// console.log("export",code);
|
||||
}
|
||||
// 去除 setup 函数,保留其中的内容
|
||||
const setupResult = code.match(setupRe);
|
||||
if (setupResult) {
|
||||
code = code.replace(setupRe, trimBr(setupResult[1]));
|
||||
// console.log("setup",code);
|
||||
}
|
||||
}
|
||||
// 替换 layer 引入语句
|
||||
// playground 中使用最新版 layer 请从 @layui/layer-vue 引入
|
||||
if (code.match(layerRe)) {
|
||||
code = code.replace(layerRe, `import { layer } from "@layui/layui-vue"`);
|
||||
// console.log("layer",code);
|
||||
}
|
||||
const originCode = {
|
||||
[MAIN_FILE_NAME]: code,
|
||||
};
|
||||
|
||||
const encoded = utoa(JSON.stringify(originCode));
|
||||
const link = `https://layui-vue.gitee.io/sandbox-vue/#${encoded}`;
|
||||
return {
|
||||
code,
|
||||
encoded,
|
||||
link,
|
||||
};
|
||||
};
|
||||
|
||||
//编码
|
||||
function utoa(data: string): string {
|
||||
return btoa(unescape(encodeURIComponent(data)));
|
||||
}
|
||||
|
||||
// 去除字符串两端的空白行
|
||||
function trimBr(str: string): string {
|
||||
return str.replace(/(^[\r\n]*)|([\r\n]*$)/, "");
|
||||
}
|
||||
Reference in New Issue
Block a user