mirror of
https://github.com/jaywcjlove/wxmp.git
synced 2026-01-08 14:28:47 +08:00
feat: init web app.
This commit is contained in:
22
.gitignore
vendored
22
.gitignore
vendored
@@ -1,11 +1,31 @@
|
||||
dist
|
||||
build
|
||||
lib
|
||||
cjs
|
||||
esm
|
||||
node_modules
|
||||
|
||||
npm-debug.log*
|
||||
lerna-debug.log
|
||||
yarn-error.log
|
||||
package-lock.json
|
||||
|
||||
.DS_Store
|
||||
.cache
|
||||
.vscode
|
||||
.idea
|
||||
.env
|
||||
|
||||
*.mpassword
|
||||
*.bak
|
||||
*.tem
|
||||
*.temp
|
||||
#.swp
|
||||
*.*~
|
||||
~*.*
|
||||
*.crx
|
||||
|
||||
# IDEA
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
50
.kktrc.ts
Normal file
50
.kktrc.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import path from 'path';
|
||||
import webpack, { Configuration } from 'webpack';
|
||||
import lessModules from '@kkt/less-modules';
|
||||
import { mdCodeModulesLoader } from 'markdown-react-code-preview-loader';
|
||||
import scopePluginOptions from '@kkt/scope-plugin-options';
|
||||
import { LoaderConfOptions } from 'kkt';
|
||||
import raw from '@kkt/raw-modules';
|
||||
import pkg from './package.json';
|
||||
|
||||
export default (conf: Configuration, env: 'development' | 'production', options: LoaderConfOptions) => {
|
||||
conf = lessModules(conf, env, options);
|
||||
conf = mdCodeModulesLoader(conf);
|
||||
conf = raw(conf, env, {
|
||||
...options,
|
||||
test: /\.(md.css)$/i,
|
||||
});
|
||||
conf = scopePluginOptions(conf, env, {
|
||||
...options,
|
||||
allowedFiles: [path.resolve(process.cwd(), 'README.md'), path.resolve(process.cwd(), 'src')],
|
||||
});
|
||||
conf.plugins!.push(
|
||||
new webpack.DefinePlugin({
|
||||
VERSION: JSON.stringify(pkg.version),
|
||||
}),
|
||||
);
|
||||
|
||||
conf.module!.exprContextCritical = false;
|
||||
if (env === 'production') {
|
||||
conf.output = { ...conf.output, publicPath: './' };
|
||||
conf.optimization = {
|
||||
...conf.optimization,
|
||||
splitChunks: {
|
||||
cacheGroups: {
|
||||
reactvendor: {
|
||||
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
|
||||
name: 'react-vendor',
|
||||
chunks: 'all',
|
||||
},
|
||||
refractor: {
|
||||
test: /[\\/]node_modules[\\/](refractor)[\\/]/,
|
||||
name: 'refractor-prismjs-vendor',
|
||||
chunks: 'all',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return conf;
|
||||
};
|
||||
88
README.md
88
README.md
@@ -1,41 +1,75 @@
|
||||
# Wxmp
|
||||
<div align="center">
|
||||
|
||||
chrome 小插件,优化在微信公众账号中发文章,因复制粘贴带过去的`font-family` CSS 样式,导致被微信过滤样式全无。使用此插件删除提交文章上所有HTML节点上的`font-family`,让复制过去的样式保持一致。
|
||||
<h1 align="center">微信公众号 Markdown 编辑器</h1>
|
||||
|
||||
目前删除这些标签上的`font-family`样式
|
||||
</div>
|
||||
|
||||
> `code`,`pre`,`h1`,`h2`,`h3`,`h4`,`h5`,`h6`,`p`,`div`,`span`
|
||||
用于微信公众号文章使用 markdown 语法做一篇简介美观大方的微信公众号图文的工具。原先是一个 Chrome 插件来解决排版问题,由于发版本麻烦,和一些功能拓展开发停滞了,最近写了一个 Web 版本供自己使用。
|
||||
|
||||

|
||||
## 功能特性
|
||||
|
||||
开发计划和一些功能介绍,有新需求可以在 issue 中提。
|
||||
|
||||
## 已经实现功能
|
||||
- [x] 支持 Markdown 所有基础语法
|
||||
- [x] 支持自定义 CSS 样式
|
||||
- [ ] 支持主题选择 & 配置。
|
||||
- [x] 支持明暗两种主题预览。
|
||||
- [ ] 支持色盘取色,快速替换文章整体色调
|
||||
|
||||
- [x] 过滤 `font-family`;
|
||||
- [x] 代码高亮区域有背景颜色;
|
||||
- [x] 代码高亮区域有横向滚动条强制不换行;
|
||||
- [x] 增加iOS滚动滚动弹性;
|
||||
- [ ] 添加设置标题工具;
|
||||
- [ ] 添加字段高亮工具;
|
||||
- [ ] 添加删除线工具,如:<del>删除线</del>;
|
||||
### 支持代码块样式
|
||||
|
||||
# 直接安装
|
||||
下面是 `jsx` 代码块展示示例,并高亮代码
|
||||
|
||||
1. 下载扩展程序[Wxmp.crx](https://github.com/jaywcjlove/wxmp/releases) 文件
|
||||
2. 在chrome里面器地址输入`chrome://extensions/` 打开插件界面
|
||||
3. 将`Wxmp.crx`文件拖入chrome浏览器的扩展程序列表中
|
||||
```jsx
|
||||
function Demo() {
|
||||
return <div className="demo">Hello World!</div>
|
||||
}
|
||||
```
|
||||
|
||||
# 开发模式插件安装
|
||||
下面是 `css` 代码块展示示例,并高亮代码
|
||||
|
||||
1. 下载文件压缩包解压
|
||||
2. 在chrome里面器地址输入`chrome://extensions/` 打开插件界面
|
||||
3. 点击`加载已解压的扩展程序...`
|
||||
4. 选择插件所在的目录
|
||||
```css
|
||||
li {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
line-height: 26px;
|
||||
color: rgb(30 41 59);
|
||||
font-family:-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif;
|
||||
}
|
||||
```
|
||||
|
||||
### 支持内联代码
|
||||
|
||||
# 使用方法
|
||||
Inline Code `{code: 0}`
|
||||
|
||||
1. 打开微信公众平台,新建图文消息,复制文章到编辑器中
|
||||
2. 在右上角点击微信图标
|
||||
3. 点击弹出的模态框上的删除按钮
|
||||
4. 如果成功会在按钮后面提示`更改成功!!`
|
||||
### 支持表格
|
||||
|
||||
表格无法使用自定义样式,暂时没找到解决途径
|
||||
|
||||
| Header 1 | Header 2 |
|
||||
| --- | --- |
|
||||
| Key 1 | Value 1 |
|
||||
| Key 2 | Value 2 |
|
||||
| Key 3 | Value 3 |
|
||||
|
||||
### 支持 GFM 脚注
|
||||
|
||||
这是一个简单的脚注[^1]。 页面最后有一些额外的文字描述。注意这不是完整的注脚[^2]。
|
||||
|
||||
[^1]: https://github.github.com/gfm/
|
||||
[^2]: 微信文章不支持锚点跳转和打开第三方 URL 超链接,所以不支持完整的注脚
|
||||
|
||||
### 支持注释
|
||||
|
||||
<ruby>
|
||||
汉 <rp></rp><rt>Han</rt><rp></rp>
|
||||
字 <rp></rp><rt>zi</rt><rp></rp>
|
||||
拼 <rp></rp><rt>pin</rt><rp></rp>
|
||||
音 <rp></rp><rt>yin</rt><rp></rp>
|
||||
注 <rp></rp><rt>zhu</rt><rp></rp>
|
||||
音 <rp></rp><rt>yin</rt><rp></rp>
|
||||
</ruby>
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the MIT License.
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
ul,li{margin: 0;padding: 0;}
|
||||
.warpper{width: 200px; min-height: 23px;}
|
||||
.warpper ul {}
|
||||
.warpper ul li{}
|
||||
.warpper ul li a{display: block;box-shadow: 0 1px 1px 0 #D0D0D0;line-height: 23px;border:1px solid #D0D0D0;border-radius: 4px;padding: 0 5px;color:#333;}
|
||||
.warpper ul li a:hover{box-shadow: 0 1px 1px 0 #4A90E2;border: 1px solid #4A90E2;color:#4A90E2;}
|
||||
.warpper ul li a:active{box-shadow: 0 1px 1px 0 #ABABAB;border: 1px solid #ABABAB;color:#ABABAB;}
|
||||
BIN
img/icon.png
BIN
img/icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB |
37
js/bg.js
37
js/bg.js
@@ -1,37 +0,0 @@
|
||||
chrome.extension.onRequest.addListener(
|
||||
function (request, sender, sendResponse) {
|
||||
if (request.hello == "btn_del_family") {
|
||||
var bodys = document.querySelectorAll('iframe#ueditor_0')
|
||||
if(bodys&&bodys.length>0&&bodys[0].contentDocument){
|
||||
var body = bodys[0].contentDocument;
|
||||
if(body){
|
||||
var elms = body.querySelectorAll('pre code,pre,h1,h2,h3,h4,h5,h6,p,div,span');
|
||||
for (var i = 0; i < elms.length; i++) {
|
||||
var styl = elms[i].getAttribute('style');
|
||||
if(elms[i].tagName === 'PRE'){
|
||||
elms[i].setAttribute('style','box-sizing: border-box; overflow: auto;font-size: 0.93em; padding: 1em; margin-top: 1.5em; margin-bottom: 1.5em; line-height: 1.3; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); border: none; border-radius: 3px; max-height: 35em; position: relative;background-color:#EDEDED;word-wrap: initial!important;-webkit-overflow-scrolling: touch;')
|
||||
}else if(elms[i].tagName === 'CODE'){
|
||||
elms[i].setAttribute('style','box-sizing: border-box;font-size: 1em; color: inherit; border-radius: 0px; white-space: inherit; overflow-wrap: normal; background: none;word-wrap:normal!important;')
|
||||
}else{
|
||||
if(styl){
|
||||
styl = styl.replace(/font-family\:[\s\S]*?\;/g,'');
|
||||
if(elms[i].tagName === 'SPAN') styl += 'word-wrap:normal!important;';
|
||||
elms[i].setAttribute('style',styl);
|
||||
}
|
||||
}
|
||||
}
|
||||
var elms = body.querySelectorAll('p code');
|
||||
for (var i = 0; i < elms.length; i++) {
|
||||
if(elms[i].tagName === 'CODE'){
|
||||
elms[i].setAttribute('style','color: #c7254e;background-color: #f9f2f4;padding: 2px 4px;border-radius: 3px;')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
sendResponse({ msg: '更改完毕!!' });
|
||||
}
|
||||
}
|
||||
);
|
||||
4
js/jq.3.0.0.min.js
vendored
4
js/jq.3.0.0.min.js
vendored
File diff suppressed because one or more lines are too long
20
js/popup.js
20
js/popup.js
@@ -1,20 +0,0 @@
|
||||
$('.warpper a.btn_del_family').on('click',function(){
|
||||
var self = this
|
||||
chrome.tabs.getSelected(null, function (tab) {
|
||||
chrome.tabs.sendRequest(tab.id, {"hello": "btn_del_family"}, function (response) {
|
||||
$(self).find('span').html(response.msg);
|
||||
setTimeout(function(){
|
||||
$(self).find('span').html('');
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
$('.warpper a.btn_del_fonts').on('click',function(){
|
||||
console.log("btn_del_fonts");
|
||||
chrome.tabs.getSelected(null, function (tab) {
|
||||
chrome.tabs.sendRequest(tab.id, {"hello": "btn_del_fonts"}, function (response) {
|
||||
console.log("response",response);
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
{
|
||||
"name": "Wxmp",
|
||||
"version": "1.0",
|
||||
"description": "微信公众账号发文章优化文章插件",
|
||||
"icons": { "128": "img/icon.png" },
|
||||
"permissions": ["*://*/*","declarativeContent","tabs", "unlimitedStorage"],
|
||||
"browser_action": {
|
||||
"default_title": "",
|
||||
"default_icon": "img/icon.png",
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"manifest_version": 2,
|
||||
"content_scripts": [{"matches": ["*://*/*"],"js": ["js/bg.js"]}],
|
||||
"permissions": ["*://*/*","tabs"]
|
||||
}
|
||||
61
package.json
Normal file
61
package.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "website",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "kkt start",
|
||||
"build": "kkt build"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.9",
|
||||
"@uiw/react-back-to-top": "^1.2.0",
|
||||
"@uiw/react-github-corners": "^1.5.15",
|
||||
"@uiw/react-markdown-editor": "^5.3.2",
|
||||
"@uiw/react-markdown-preview": "^4.1.0",
|
||||
"@wcj/dark-mode": "^1.0.15",
|
||||
"css-tree": "^2.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-code-preview-layout": "^2.0.4",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.3.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"rehype-prism-plus": "^1.5.0",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"rehype-stringify": "^9.0.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-parse": "^10.0.1",
|
||||
"remark-rehype": "^10.1.0",
|
||||
"styled-components": "^5.3.5",
|
||||
"unified": "^10.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kkt/less-modules": "^7.2.0",
|
||||
"@kkt/raw-modules": "^7.2.0",
|
||||
"@kkt/scope-plugin-options": "^7.2.0",
|
||||
"@types/css-tree": "^1.0.7",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"kkt": "^7.2.0",
|
||||
"markdown-react-code-preview-loader": "^2.1.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
18
popup.html
18
popup.html
@@ -1,18 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>弹出层</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/popup.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="warpper">
|
||||
<ul>
|
||||
<li><a class="btn_del_family" href="#">优化文章样式<span></span></a></li>
|
||||
<!-- <li><a class="btn_del_fonts" href="#">删除family样式</a></li> -->
|
||||
</ul>
|
||||
</div>
|
||||
<script type="text/javascript" src="js/jq.3.0.0.min.js"></script>
|
||||
<script type="text/javascript" src="js/popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
24
public/index.html
Normal file
24
public/index.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<title>微信公众号 Markdown 编辑器</title>
|
||||
<meta name="keywords" content="react,simple,monorepo,template,component,project,package,development" />
|
||||
<meta name="description" content="Simple React package development project example template." />
|
||||
<link rel="icon" href="%PUBLIC_URL%favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
37
re.md
Normal file
37
re.md
Normal file
@@ -0,0 +1,37 @@
|
||||
<div align="center">
|
||||
|
||||
<h1 align="center">微信公众号 Markdown 编辑器</h1>
|
||||
|
||||
</div>
|
||||
|
||||
用于微信公众号文章使用 markdown 语法做一篇简介美观大方的微信公众号图文的工具。原先是一个 Chrome 插件来解决排版问题,由于发版本麻烦,和一些功能拓展开发停滞了,最近写了一个 Web 版本供自己使用。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- [x] 支持 Markdown 所有基础语法
|
||||
- [x] 支持自定义 CSS 样式
|
||||
- [ ] 支持主题选择 & 配置。
|
||||
- [x] 支持明暗两种主题预览。
|
||||
- [ ] 支持色盘取色,快速替换文章整体色调
|
||||
|
||||
### 支持代码块样式
|
||||
|
||||
```jsx
|
||||
function Demo() {
|
||||
return <div>Hello World!</div>
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
li {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
line-height: 26px;
|
||||
color: rgb(30 41 59);
|
||||
font-family:-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif;
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the MIT License.
|
||||
13
src/App.tsx
Normal file
13
src/App.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import { Layout } from './components/Layout';
|
||||
import { HomePage } from './pages/home';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<Layout />}>
|
||||
<Route index element={<HomePage />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
3
src/assets/github.svg
Normal file
3
src/assets/github.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 1024 1024" height="1em" width="1em">
|
||||
<path d="M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9a127.5 127.5 0 0 1 38.1 91v112.5c.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 784 B |
3
src/assets/logo.svg
Normal file
3
src/assets/logo.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="#1aad1a" viewBox="0 0 1024 1024" height="1em" width="1em">
|
||||
<path d="M690.1 377.4c5.9 0 11.8.2 17.6.5-24.4-128.7-158.3-227.1-319.9-227.1C209 150.8 64 271.4 64 420.2c0 81.1 43.6 154.2 111.9 203.6a21.5 21.5 0 0 1 9.1 17.6c0 2.4-.5 4.6-1.1 6.9-5.5 20.3-14.2 52.8-14.6 54.3-.7 2.6-1.7 5.2-1.7 7.9 0 5.9 4.8 10.8 10.8 10.8 2.3 0 4.2-.9 6.2-2l70.9-40.9c5.3-3.1 11-5 17.2-5 3.2 0 6.4.5 9.5 1.4 33.1 9.5 68.8 14.8 105.7 14.8 6 0 11.9-.1 17.8-.4-7.1-21-10.9-43.1-10.9-66 0-135.8 132.2-245.8 295.3-245.8zm-194.3-86.5c23.8 0 43.2 19.3 43.2 43.1s-19.3 43.1-43.2 43.1c-23.8 0-43.2-19.3-43.2-43.1s19.4-43.1 43.2-43.1zm-215.9 86.2c-23.8 0-43.2-19.3-43.2-43.1s19.3-43.1 43.2-43.1 43.2 19.3 43.2 43.1-19.4 43.1-43.2 43.1zm586.8 415.6c56.9-41.2 93.2-102 93.2-169.7 0-124-120.8-224.5-269.9-224.5-149 0-269.9 100.5-269.9 224.5S540.9 847.5 690 847.5c30.8 0 60.6-4.4 88.1-12.3 2.6-.8 5.2-1.2 7.9-1.2 5.2 0 9.9 1.6 14.3 4.1l59.1 34c1.7 1 3.3 1.7 5.2 1.7a9 9 0 0 0 6.4-2.6 9 9 0 0 0 2.6-6.4c0-2.2-.9-4.4-1.4-6.6-.3-1.2-7.6-28.3-12.2-45.3-.5-1.9-.9-3.8-.9-5.7.1-5.9 3.1-11.2 7.6-14.5zM600.2 587.2c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9c0 19.8-16.2 35.9-36 35.9zm179.9 0c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9a36.08 36.08 0 0 1-36 35.9z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
77
src/components/Layout.tsx
Normal file
77
src/components/Layout.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import styled from 'styled-components';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import '@wcj/dark-mode';
|
||||
import { ReactComponent as LogoIcon } from '../assets/logo.svg';
|
||||
import { ReactComponent as GithubIcon } from '../assets/github.svg';
|
||||
|
||||
const Warpper = styled.div`
|
||||
`;
|
||||
|
||||
const Header = styled.header`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--color-border-muted);
|
||||
padding: 0.5rem 1rem 0.5rem 1rem;
|
||||
`;
|
||||
|
||||
const Article = styled.article`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
`;
|
||||
|
||||
const Logo = styled(LogoIcon)`
|
||||
max-width: 3.6rem;
|
||||
`;
|
||||
|
||||
const Title = styled.h1`
|
||||
font-size: 1.0rem;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
sup {
|
||||
color: var(--color-fg-subtle);
|
||||
margin-left: 0.4rem;
|
||||
background-color: var(--color-border-muted);
|
||||
border-radius: 0.1rem;
|
||||
padding: 0 0.2rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
`;
|
||||
|
||||
const Section = styled.section`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
dark-mode {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
a svg {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
export function Layout() {
|
||||
return (
|
||||
<Warpper className="wmde-markdown-color">
|
||||
<Header>
|
||||
<Article>
|
||||
<Logo width={28} height={28} />
|
||||
<Title>
|
||||
微信公众号排版编辑器
|
||||
<sup> v{VERSION} </sup>
|
||||
</Title>
|
||||
</Article>
|
||||
<Section>
|
||||
<dark-mode permanent dark="Dark" light="Light" />
|
||||
<a href="https://github.com/jaywcjlove/wxmp" target="__blank">
|
||||
<GithubIcon width={28} height={28} />
|
||||
</a>
|
||||
</Section>
|
||||
</Header>
|
||||
<Outlet />
|
||||
</Warpper>
|
||||
);
|
||||
}
|
||||
177
src/conf/default.md.css
Normal file
177
src/conf/default.md.css
Normal file
@@ -0,0 +1,177 @@
|
||||
a {
|
||||
color: #576b95;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align:center;
|
||||
color:#3f3f3f;
|
||||
line-height:1.75;
|
||||
font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
|
||||
font-size:1.2em;
|
||||
font-weight:bold;
|
||||
display:table;
|
||||
margin:2em auto 1em;
|
||||
padding:0 1em;
|
||||
border-bottom:2px solid #009874;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align:center;
|
||||
color:#fff;
|
||||
line-height:1.75;
|
||||
font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
|
||||
font-size:1.2em;
|
||||
font-weight:bold;
|
||||
display:table;
|
||||
margin:4em auto 2em;
|
||||
padding:0 0.3em;
|
||||
border-radius: 0.3rem;
|
||||
background:#009874;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align:left;
|
||||
color:#3f3f3f;
|
||||
line-height:1.2;
|
||||
font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
|
||||
font-size:1.1em;
|
||||
font-weight:bold;
|
||||
margin:2em 8px 0.75em 0;
|
||||
padding-left:8px;
|
||||
border-left:3px solid #009874;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 1.2em;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
ol {
|
||||
padding-left: 1.2em;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
line-height: 26px;
|
||||
color: rgb(30 41 59);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
text-align:left;
|
||||
line-height:1.75;
|
||||
font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
|
||||
font-size:14px;
|
||||
font-style:normal;
|
||||
border-left:none;
|
||||
padding:1em;
|
||||
border-radius:4px;
|
||||
background:rgba(27,31,35,.05);
|
||||
margin:2em 8px;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 1em;
|
||||
color: rgb(51, 51, 51);
|
||||
background: rgb(248, 248, 248);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-variant-ligatures: normal;
|
||||
font-variant-caps: normal;
|
||||
font-weight: 400;
|
||||
letter-spacing: normal;
|
||||
orphans: 2;
|
||||
text-indent: 0px;
|
||||
text-transform: none;
|
||||
widows: 2;
|
||||
word-spacing: 0px;
|
||||
text-decoration-style: initial;
|
||||
text-decoration-color: initial;
|
||||
text-align: left;
|
||||
line-height: 1.5;
|
||||
font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
||||
border-radius: 5px;
|
||||
margin: 0.9rem 0;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100% !important;
|
||||
border-collapse: collapse;
|
||||
line-height: 1.35;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
td {
|
||||
border: 1px solid #DDD;
|
||||
padding: 0.25em 0.5em;
|
||||
}
|
||||
|
||||
th {
|
||||
background: rgb(0 0 0 / 5%);
|
||||
border: 1px solid #DDD;
|
||||
padding: 0.25em 0.5em;
|
||||
}
|
||||
|
||||
.code-highlight {
|
||||
text-align: left;
|
||||
line-height: 1.75;
|
||||
font-family: Menlo, "Operator Mono", Consolas, Monaco, monospace;
|
||||
font-size: 14px;
|
||||
margin: 0px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.code-line {
|
||||
display: block;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.code-spans {
|
||||
text-align:left;
|
||||
line-height:1;
|
||||
white-space:pre;
|
||||
color: #009874;
|
||||
background:rgba(27,31,35,.05);
|
||||
padding: 0.2rem 0.3rem;
|
||||
border-radius:4px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.footnotes-title {
|
||||
font-family:-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;
|
||||
font-size:1.0rem;
|
||||
font-weight:bold;
|
||||
display:table;
|
||||
margin:3rem 0 0.6rem 0;
|
||||
padding-left: 0.2rem;
|
||||
}
|
||||
|
||||
.footnotes-list {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.comment { color: #6a737d; }
|
||||
.property { color: #6f42c1; }
|
||||
.function { color: #6f42c1; }
|
||||
.keyword { color: #d73a49; }
|
||||
.punctuation { color: #0550ae; }
|
||||
.unit { color: #0550ae; }
|
||||
.tag { color: #22863a; }
|
||||
.selector { color: #22863a; }
|
||||
.quote { color: #22863a; }
|
||||
.number { color: #005cc5; }
|
||||
.attr-name { color: #005cc5; }
|
||||
.attr-value { color: #005cc5; }
|
||||
61
src/index.tsx
Normal file
61
src/index.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { HashRouter } from 'react-router-dom';
|
||||
import BackToUp from '@uiw/react-back-to-top';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import App from './App';
|
||||
|
||||
export const GlobalStyle = createGlobalStyle`
|
||||
[data-color-mode*='dark'], [data-color-mode*='dark'] body {
|
||||
--color-fg-default: #c9d1d9;
|
||||
--color-fg-muted: #8b949e;
|
||||
--color-fg-subtle: #484f58;
|
||||
--color-canvas-default: #0d1117;
|
||||
--color-canvas-subtle: #161b22;
|
||||
--color-border-default: #30363d;
|
||||
--color-border-muted: #21262d;
|
||||
--color-neutral-muted: rgba(110,118,129,0.4);
|
||||
--color-accent-fg: #58a6ff;
|
||||
--color-accent-emphasis: #1f6feb;
|
||||
--color-attention-subtle: rgba(187,128,9,0.15);
|
||||
--color-danger-fg: #f85149;
|
||||
}
|
||||
[data-color-mode*='light'], [data-color-mode*='light'] body {
|
||||
--color-fg-default: #24292f;
|
||||
--color-fg-muted: #57606a;
|
||||
--color-fg-subtle: #6e7781;
|
||||
--color-canvas-default: #ffffff;
|
||||
--color-canvas-subtle: #f6f8fa;
|
||||
--color-border-default: #d0d7de;
|
||||
--color-border-muted: hsla(210,18%,87%,1);
|
||||
--color-neutral-muted: rgba(175,184,193,0.2);
|
||||
--color-accent-fg: #0969da;
|
||||
--color-accent-emphasis: #0969da;
|
||||
--color-attention-subtle: #fff8c5;
|
||||
--color-danger-fg: #cf222e;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`;
|
||||
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container!);
|
||||
root.render(
|
||||
<HashRouter>
|
||||
<Toaster />
|
||||
<BackToUp>Top</BackToUp>
|
||||
<GlobalStyle />
|
||||
<App />
|
||||
</HashRouter>,
|
||||
);
|
||||
17
src/pages/home/Preview.tsx
Normal file
17
src/pages/home/Preview.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { MarkdownPreviewProps } from '@uiw/react-markdown-preview';
|
||||
import styled from 'styled-components';
|
||||
import def from '../../conf/default.md.css';
|
||||
|
||||
import { markdownToHTML } from '../../utils/markdownToHTML';
|
||||
|
||||
const Warpper = styled.div`
|
||||
width: 375px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 0 60px rgb(0 0 0 / 10%);
|
||||
min-height: 100%;
|
||||
`;
|
||||
|
||||
export const Preview = (props: MarkdownPreviewProps, visible: boolean) => {
|
||||
const html = markdownToHTML(props.source || '', def);
|
||||
return <Warpper dangerouslySetInnerHTML={{ __html: html }} />;
|
||||
}
|
||||
45
src/pages/home/copy.tsx
Normal file
45
src/pages/home/copy.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import { ICommand, IMarkdownEditor, ToolBarProps } from '@uiw/react-markdown-editor';
|
||||
import toast from 'react-hot-toast';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Button = styled.button`
|
||||
white-space: nowrap;
|
||||
width: initial !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 0.4rem !important;
|
||||
`;
|
||||
|
||||
const CopyView: React.FC<{ command: ICommand; editorProps: IMarkdownEditor & ToolBarProps }> = (props) => {
|
||||
const { editorProps } = props;
|
||||
const handleClick = () => {
|
||||
const dom = editorProps.preview.current;
|
||||
dom?.focus();
|
||||
window.getSelection()?.removeAllRanges();
|
||||
let range = document.createRange();
|
||||
range.setStartBefore(dom?.firstChild!);
|
||||
range.setEndAfter(dom?.lastChild!);
|
||||
window.getSelection()?.addRange(range);
|
||||
document.execCommand(`copy`);
|
||||
window.getSelection()?.removeAllRanges();
|
||||
toast.success(<div>复制成功!去公众号编辑器复制吧!</div>);
|
||||
}
|
||||
return (
|
||||
<Button type="button" onClick={handleClick}>
|
||||
{props.command.icon} 复制
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export const copy: ICommand = {
|
||||
name: 'copy',
|
||||
keyCommand: 'copy',
|
||||
button: (command, props, opts) => <CopyView command={command} editorProps={{ ...props, ...opts }} />,
|
||||
icon: (
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" height="16" width="16">
|
||||
<path d="M20 2H10a2 2 0 0 0-2 2v2h8a2 2 0 0 1 2 2v8h2a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z"/>
|
||||
<path d="M4 22h10c1.103 0 2-.897 2-2V10c0-1.103-.897-2-2-2H4c-1.103 0-2 .897-2 2v10c0 1.103.897 2 2 2zm2-10h6v2H6v-2zm0 4h6v2H6v-2z"/>
|
||||
</svg>
|
||||
),
|
||||
};
|
||||
27
src/pages/home/index.tsx
Normal file
27
src/pages/home/index.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import MarkdownEditor, { getCommands } from '@uiw/react-markdown-editor';
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import styled from 'styled-components';
|
||||
import data from '../../../README.md';
|
||||
import { Preview } from './Preview';
|
||||
import { copy } from './copy'
|
||||
|
||||
const Warpper = styled.div`
|
||||
height: calc(100vh - 2.9rem);
|
||||
`;
|
||||
|
||||
export const HomePage = () => {
|
||||
const commands = getCommands();
|
||||
return (
|
||||
<Warpper>
|
||||
<MarkdownEditor
|
||||
value={data.source}
|
||||
toolbars={commands}
|
||||
toolbarsMode={[copy, 'preview', 'fullscreen']}
|
||||
extensions={[EditorView.lineWrapping]}
|
||||
renderPreview={Preview}
|
||||
visible={true}
|
||||
height="calc(100vh - 5.0rem)"
|
||||
/>
|
||||
</Warpper>
|
||||
);
|
||||
}
|
||||
19
src/react-app-env.d.ts
vendored
Normal file
19
src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/// <reference types="react-scripts" />
|
||||
|
||||
declare module '*.less' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare var VERSION: string;
|
||||
|
||||
declare module '*.md' {
|
||||
import { CodeBlockData } from 'markdown-react-code-preview-loader';
|
||||
const src: CodeBlockData;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.md.css' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
90
src/utils/css.ts
Normal file
90
src/utils/css.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { RootContent, Element, Text } from 'hast';
|
||||
|
||||
export const getBlock = (data: any, str: string = '') => {
|
||||
if (data && data.data && data.data.type === 'Declaration') {
|
||||
str = `${data.data.property}: ${data.data.value.value}${data.data.important ? ' !important' : ''};`;
|
||||
if (data.next) {
|
||||
str += getBlock(data.next)
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export const cssdata = (list: any, result: Record<string, string> = {}) => {
|
||||
if (list.data && list.data.type === 'Rule') {
|
||||
result[list.data.prelude.value] = getBlock(list.data.block.children.head);
|
||||
if (list.next) {
|
||||
result = cssdata(list.next, {...result})
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export const spaceEscape = (node: RootContent) => {
|
||||
if (node.type === 'element' && node.children) {
|
||||
const className = (node.properties?.className as string[]);
|
||||
if (className) {
|
||||
if (!node.properties) {
|
||||
node.properties = {};
|
||||
}
|
||||
node.properties.className = className.filter((str: string) => !/(token|control-flow)/.test(str));
|
||||
}
|
||||
|
||||
node.children.map(elm => {
|
||||
if (elm.type === 'element' && elm.children) {
|
||||
spaceEscape(elm)
|
||||
}
|
||||
if (elm.type === 'text') {
|
||||
elm.value = elm.value.replace(/\s/g, '\u00A0')
|
||||
}
|
||||
return elm
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type ChildContent = Element | Text;
|
||||
const getNodeText = (node: ChildContent[]) => {
|
||||
let str = '';
|
||||
node.forEach((item) => {
|
||||
if (item.type === 'text') str += item.value;
|
||||
else if (item.type === 'element') {
|
||||
str += getNodeText(item.children as ChildContent[]);
|
||||
}
|
||||
})
|
||||
return str.replace(/↩/, '');
|
||||
}
|
||||
|
||||
export const footnotes = (node: Element) => {
|
||||
node.children.map((item) => {
|
||||
if (item.type === 'element' && item.tagName === 'h2') {
|
||||
if (!item.properties) item.properties = {};
|
||||
item.properties.className = ['footnotes-title'];
|
||||
item.children = [{
|
||||
type: 'text',
|
||||
value: '参考'
|
||||
}];
|
||||
}
|
||||
if (item.type === 'element' && item.tagName === 'ol') {
|
||||
item.children.map((li) => {
|
||||
if (li.type === 'element' && li.tagName === 'li') {
|
||||
if (!li.properties) li.properties = {};
|
||||
li.properties.className = ['footnotes-list'];
|
||||
li.children = [{
|
||||
type: 'text',
|
||||
value: getNodeText(li.children as ChildContent[])
|
||||
}]
|
||||
}
|
||||
return li;
|
||||
})
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}
|
||||
|
||||
export const footnotesLabel = (node: Element) => {
|
||||
const label = getNodeText(node.children as ChildContent[]);
|
||||
node.children = [{
|
||||
type: 'text',
|
||||
value: `[${label}]`
|
||||
}];
|
||||
}
|
||||
80
src/utils/markdownToHTML.ts
Normal file
80
src/utils/markdownToHTML.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { VFile } from 'vfile';
|
||||
import { unified } from 'unified';
|
||||
import * as csstree from 'css-tree';
|
||||
import { Element } from 'hast';
|
||||
import remarkParse from 'remark-parse';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkRehype from 'remark-rehype';
|
||||
import rehypePrism from 'rehype-prism-plus';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeRewrite from 'rehype-rewrite';
|
||||
import stringify from 'rehype-stringify';
|
||||
import { cssdata, spaceEscape, footnotes, footnotesLabel } from './css';
|
||||
|
||||
export type MarkdownToHTMLOptions = {
|
||||
|
||||
}
|
||||
|
||||
export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLOptions = {}) {
|
||||
const ast = csstree.parse(css, {
|
||||
parseAtrulePrelude: false,
|
||||
parseRulePrelude: false,
|
||||
parseValue: false,
|
||||
parseCustomProperty: false,
|
||||
positions: false
|
||||
});
|
||||
// @ts-ignore
|
||||
const data = cssdata(ast.children.head);
|
||||
const processor = unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkRehype, { allowDangerousHtml: true })
|
||||
.use(rehypePrism)
|
||||
.use(remarkGfm)
|
||||
.use(rehypeRaw)
|
||||
.use(rehypeRewrite, {
|
||||
rewrite: (node, index, parent) => {
|
||||
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName === 'pre') {
|
||||
spaceEscape(node)
|
||||
}
|
||||
if (node?.type === 'element' && node.tagName === 'section' && (node?.properties?.className as string[]).includes('footnotes')) {
|
||||
footnotes(node)
|
||||
}
|
||||
if (node?.type === 'element' && node.tagName === 'sup') {
|
||||
footnotesLabel(node)
|
||||
}
|
||||
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName !== 'pre') {
|
||||
if (!node.properties) node.properties = {}
|
||||
node.properties!.className = ['code-spans'];
|
||||
}
|
||||
if (node?.type === 'element') {
|
||||
if (node.tagName === 'input' && parent?.type === 'element') {
|
||||
if (parent && parent.type === 'element') {
|
||||
parent.children = parent?.children.filter(elm => (elm as Element).tagName !== 'input')
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!node.properties) {
|
||||
node.properties = {};
|
||||
}
|
||||
const className = (node.properties?.className as string[]);
|
||||
let style = '';
|
||||
if (className) {
|
||||
className.forEach((name) => {
|
||||
if (data[`.${name}`]) {
|
||||
style = data[`.${name}`];
|
||||
}
|
||||
})
|
||||
}
|
||||
if (!style) style = data[node.tagName];
|
||||
if (style) {
|
||||
node.properties.style = style;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.use(stringify);
|
||||
const file = new VFile();
|
||||
file.value = md;
|
||||
const hastNode = processor.runSync(processor.parse(file), file);
|
||||
return String(processor.stringify(hastNode, file));
|
||||
}
|
||||
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"include": [".kktrc.ts", "src"],
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"target": "esnext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"declaration": true,
|
||||
"baseUrl": "./src",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user