mirror of
https://github.com/jaywcjlove/wxmp.git
synced 2026-01-08 14:28:47 +08:00
chore: format code & add format tools.
This commit is contained in:
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx pretty-quick --staged
|
||||
13
.prettierignore
Normal file
13
.prettierignore
Normal file
@@ -0,0 +1,13 @@
|
||||
**/*.md
|
||||
**/*.svg
|
||||
**/*.ejs
|
||||
**/*.html
|
||||
**/*.yml
|
||||
package.json
|
||||
node_modules
|
||||
dist
|
||||
build
|
||||
coverage
|
||||
lib
|
||||
esm
|
||||
test
|
||||
11
.prettierrc
Normal file
11
.prettierrc
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 120,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ".prettierrc",
|
||||
"options": { "parser": "json" }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4,7 +4,10 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "kkt start",
|
||||
"build": "kkt build"
|
||||
"build": "kkt build",
|
||||
"prepare": "husky install",
|
||||
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
|
||||
"pretty-quick": "pretty-quick --staged"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -52,6 +55,9 @@
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"husky": "^8.0.1",
|
||||
"prettier": "^2.7.1",
|
||||
"pretty-quick": "~3.1.3",
|
||||
"kkt": "^7.2.0",
|
||||
"markdown-react-code-preview-loader": "^2.1.2"
|
||||
},
|
||||
|
||||
@@ -4,8 +4,7 @@ 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 Warpper = styled.div``;
|
||||
|
||||
const Header = styled.header`
|
||||
display: flex;
|
||||
@@ -27,7 +26,7 @@ const Logo = styled(LogoIcon)`
|
||||
`;
|
||||
|
||||
const Title = styled.h1`
|
||||
font-size: 1.0rem;
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -77,4 +76,4 @@ export function Layout() {
|
||||
<Outlet />
|
||||
</Warpper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,4 @@ const Warpper = styled.div`
|
||||
export const Preview = (props: MarkdownPreviewProps, visible: boolean) => {
|
||||
const html = markdownToHTML(props.source || '', def);
|
||||
return <Warpper dangerouslySetInnerHTML={{ __html: html }} />;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ const CopyView: React.FC<{ command: ICommand; editorProps: IMarkdownEditor & Too
|
||||
document.execCommand(`copy`);
|
||||
window.getSelection()?.removeAllRanges();
|
||||
toast.success(<div>复制成功!去公众号编辑器复制吧!</div>);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Button type="button" onClick={handleClick}>
|
||||
{props.command.icon} 复制
|
||||
@@ -38,8 +38,8 @@ export const copy: ICommand = {
|
||||
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"/>
|
||||
<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>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import MarkdownEditor, { getCommands } from '@uiw/react-markdown-editor';
|
||||
import { useContext } from 'react';
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import { EditorView } from '@codemirror/view';
|
||||
import styled from 'styled-components';
|
||||
import { Preview } from './Preview';
|
||||
import { copy } from './copy';
|
||||
@@ -29,4 +29,4 @@ export const HomePage = () => {
|
||||
/>
|
||||
</Warpper>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ const Select = styled.select`
|
||||
|
||||
const ThemeView: React.FC<{ command: ICommand; editorProps: IMarkdownEditor & ToolBarProps }> = (props) => {
|
||||
const { theme, setTheme } = useContext(Context);
|
||||
const handleChange = (ev: React.ChangeEvent<HTMLSelectElement>) => setTheme(ev.target.value as any)
|
||||
const handleChange = (ev: React.ChangeEvent<HTMLSelectElement>) => setTheme(ev.target.value as any);
|
||||
return (
|
||||
<Select value={theme} onChange={handleChange}>
|
||||
<option value="abcdef">Abcdef Theme</option>
|
||||
@@ -55,8 +55,8 @@ export const theme: ICommand = {
|
||||
button: (command, props, opts) => <ThemeView 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"/>
|
||||
<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>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import { abcdef } from '@uiw/codemirror-theme-abcdef';
|
||||
import { androidstudio } from '@uiw/codemirror-theme-androidstudio';
|
||||
import { atomone } from '@uiw/codemirror-theme-atomone';
|
||||
@@ -14,9 +14,23 @@ import { sublime } from '@uiw/codemirror-theme-sublime';
|
||||
import { xcodeLight, xcodeDark } from '@uiw/codemirror-theme-xcode';
|
||||
|
||||
export const themes = {
|
||||
abcdef, androidstudio, atomone, bbedit, bespin, darcula, dracula, duotoneLight, duotoneDark, eclipse,
|
||||
githubLight, githubDark, okaidia, sublime, xcodeLight, xcodeDark
|
||||
}
|
||||
abcdef,
|
||||
androidstudio,
|
||||
atomone,
|
||||
bbedit,
|
||||
bespin,
|
||||
darcula,
|
||||
dracula,
|
||||
duotoneLight,
|
||||
duotoneDark,
|
||||
eclipse,
|
||||
githubLight,
|
||||
githubDark,
|
||||
okaidia,
|
||||
sublime,
|
||||
xcodeLight,
|
||||
xcodeDark,
|
||||
};
|
||||
|
||||
export type ThemeValue = keyof typeof themes;
|
||||
|
||||
@@ -28,24 +42,26 @@ export interface CreateContext {
|
||||
}
|
||||
|
||||
export const Context = React.createContext<CreateContext>({
|
||||
css: "",
|
||||
css: '',
|
||||
setCss: () => {},
|
||||
theme: 'githubLight',
|
||||
setTheme: () => {},
|
||||
});
|
||||
|
||||
export const Provider: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const [css, setCss] = React.useState("");
|
||||
const [theme, setTheme] = React.useState<ThemeValue>("githubLight");
|
||||
const [css, setCss] = React.useState('');
|
||||
const [theme, setTheme] = React.useState<ThemeValue>('githubLight');
|
||||
|
||||
return (
|
||||
<Context.Provider
|
||||
value={{
|
||||
css, setCss,
|
||||
theme, setTheme,
|
||||
css,
|
||||
setCss,
|
||||
theme,
|
||||
setTheme,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,25 +4,25 @@ 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)
|
||||
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})
|
||||
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[]);
|
||||
const className = node.properties?.className as string[];
|
||||
if (className) {
|
||||
if (!node.properties) {
|
||||
node.properties = {};
|
||||
@@ -30,17 +30,17 @@ export const spaceEscape = (node: RootContent) => {
|
||||
node.properties.className = className.filter((str: string) => !/(token|control-flow)/.test(str));
|
||||
}
|
||||
|
||||
node.children.map(elm => {
|
||||
node.children.map((elm) => {
|
||||
if (elm.type === 'element' && elm.children) {
|
||||
spaceEscape(elm)
|
||||
spaceEscape(elm);
|
||||
}
|
||||
if (elm.type === 'text') {
|
||||
elm.value = elm.value.replace(/\s/g, '\u00A0')
|
||||
elm.value = elm.value.replace(/\s/g, '\u00A0');
|
||||
}
|
||||
return elm
|
||||
})
|
||||
return elm;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
type ChildContent = Element | Text;
|
||||
const getNodeText = (node: ChildContent[]) => {
|
||||
@@ -50,53 +50,64 @@ const getNodeText = (node: ChildContent[]) => {
|
||||
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: '参考'
|
||||
}];
|
||||
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[])
|
||||
}]
|
||||
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}]`
|
||||
}];
|
||||
}
|
||||
node.children = [
|
||||
{
|
||||
type: 'text',
|
||||
value: `[${label}]`,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const imagesStyle = (node: Element, parent: Root | Element | null) => {
|
||||
if (parent?.type === 'element' && /(p|a)/.test(parent.tagName) && node?.type === 'element' && node.tagName === 'img') {
|
||||
if (
|
||||
parent?.type === 'element' &&
|
||||
/(p|a)/.test(parent.tagName) &&
|
||||
node?.type === 'element' &&
|
||||
node.tagName === 'img'
|
||||
) {
|
||||
if (parent.tagName === 'p') {
|
||||
parent.tagName = 'figure'
|
||||
parent.tagName = 'figure';
|
||||
}
|
||||
if (!parent.properties) parent.properties = {}
|
||||
parent.properties.className = ['image-warpper']
|
||||
if (!node.properties) node.properties = {}
|
||||
node.properties.className = ['image']
|
||||
if (!parent.properties) parent.properties = {};
|
||||
parent.properties.className = ['image-warpper'];
|
||||
if (!node.properties) node.properties = {};
|
||||
node.properties.className = ['image'];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,17 +13,15 @@ import rehypeRewrite from 'rehype-rewrite';
|
||||
import stringify from 'rehype-stringify';
|
||||
import { cssdata, spaceEscape, footnotes, footnotesLabel, imagesStyle } from './css';
|
||||
|
||||
export type MarkdownToHTMLOptions = {
|
||||
|
||||
}
|
||||
export type MarkdownToHTMLOptions = {};
|
||||
|
||||
export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLOptions = {}) {
|
||||
const ast = csstree.parse(css, {
|
||||
const ast = csstree.parse(css, {
|
||||
parseAtrulePrelude: false,
|
||||
parseRulePrelude: false,
|
||||
parseValue: false,
|
||||
parseCustomProperty: false,
|
||||
positions: false
|
||||
positions: false,
|
||||
});
|
||||
// @ts-ignore
|
||||
const data = cssdata(ast.children.head);
|
||||
@@ -33,32 +31,46 @@ export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLO
|
||||
.use(remarkRehype, { allowDangerousHtml: true })
|
||||
.use(rehypePrism)
|
||||
.use(rehypeRaw)
|
||||
.use(rehypeIgnore, { })
|
||||
.use(rehypeIgnore, {})
|
||||
.use(rehypeAttrs, { properties: 'attr' })
|
||||
.use(rehypeRewrite, {
|
||||
rewrite: (node, index, parent) => {
|
||||
// @ts-ignore
|
||||
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName === 'pre') {
|
||||
spaceEscape(node)
|
||||
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 === 'section' &&
|
||||
(node?.properties?.className as string[]).includes('footnotes')
|
||||
) {
|
||||
footnotes(node);
|
||||
}
|
||||
if (node?.type === 'element' && node.tagName === 'sup') {
|
||||
footnotesLabel(node)
|
||||
footnotesLabel(node);
|
||||
}
|
||||
if (node?.type === 'element' && node.tagName === 'img') {
|
||||
imagesStyle(node, parent)
|
||||
imagesStyle(node, parent);
|
||||
}
|
||||
// Code Spans style
|
||||
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName !== 'pre') {
|
||||
if (!node.properties) node.properties = {}
|
||||
if (
|
||||
node?.type === 'element' &&
|
||||
node?.tagName === 'code' &&
|
||||
parent?.type === 'element' &&
|
||||
parent?.tagName !== 'pre'
|
||||
) {
|
||||
if (!node.properties) node.properties = {};
|
||||
node.properties!.className = ['code-spans'];
|
||||
}
|
||||
// List TODO style
|
||||
if (parent?.type === 'element' && node?.type === 'element' && node?.tagName === 'input') {
|
||||
if (parent?.type === 'element' && node?.type === 'element' && node?.tagName === 'input') {
|
||||
if (parent && parent.type === 'element') {
|
||||
parent.children = parent?.children.filter(elm => (elm as Element).tagName !== 'input')
|
||||
parent.children = parent?.children.filter((elm) => (elm as Element).tagName !== 'input');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -67,25 +79,25 @@ export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLO
|
||||
if (!node.properties) {
|
||||
node.properties = {};
|
||||
}
|
||||
const className = (node.properties?.className as string[]);
|
||||
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 + (node.properties.style || '');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
.use(stringify);
|
||||
const file = new VFile();
|
||||
file.value = md;
|
||||
const hastNode = processor.runSync(processor.parse(file), file);
|
||||
return String(processor.stringify(hastNode, file));
|
||||
const file = new VFile();
|
||||
file.value = md;
|
||||
const hastNode = processor.runSync(processor.parse(file), file);
|
||||
return String(processor.stringify(hastNode, file));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user