chore: format code & add format tools.

This commit is contained in:
jaywcjlove
2022-09-02 22:45:28 +08:00
parent 6da12fcb10
commit d007e11f20
12 changed files with 157 additions and 85 deletions

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx pretty-quick --staged

13
.prettierignore Normal file
View File

@@ -0,0 +1,13 @@
**/*.md
**/*.svg
**/*.ejs
**/*.html
**/*.yml
package.json
node_modules
dist
build
coverage
lib
esm
test

11
.prettierrc Normal file
View File

@@ -0,0 +1,11 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 120,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}

View File

@@ -4,7 +4,10 @@
"private": true, "private": true,
"scripts": { "scripts": {
"start": "kkt start", "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", "license": "MIT",
"dependencies": { "dependencies": {
@@ -52,6 +55,9 @@
"@types/react": "^18.0.17", "@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@types/styled-components": "^5.1.25", "@types/styled-components": "^5.1.25",
"husky": "^8.0.1",
"prettier": "^2.7.1",
"pretty-quick": "~3.1.3",
"kkt": "^7.2.0", "kkt": "^7.2.0",
"markdown-react-code-preview-loader": "^2.1.2" "markdown-react-code-preview-loader": "^2.1.2"
}, },

View File

@@ -4,8 +4,7 @@ import '@wcj/dark-mode';
import { ReactComponent as LogoIcon } from '../assets/logo.svg'; import { ReactComponent as LogoIcon } from '../assets/logo.svg';
import { ReactComponent as GithubIcon } from '../assets/github.svg'; import { ReactComponent as GithubIcon } from '../assets/github.svg';
const Warpper = styled.div` const Warpper = styled.div``;
`;
const Header = styled.header` const Header = styled.header`
display: flex; display: flex;
@@ -27,7 +26,7 @@ const Logo = styled(LogoIcon)`
`; `;
const Title = styled.h1` const Title = styled.h1`
font-size: 1.0rem; font-size: 1rem;
margin: 0; margin: 0;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -77,4 +76,4 @@ export function Layout() {
<Outlet /> <Outlet />
</Warpper> </Warpper>
); );
} }

View File

@@ -14,4 +14,4 @@ const Warpper = styled.div`
export const Preview = (props: MarkdownPreviewProps, visible: boolean) => { export const Preview = (props: MarkdownPreviewProps, visible: boolean) => {
const html = markdownToHTML(props.source || '', def); const html = markdownToHTML(props.source || '', def);
return <Warpper dangerouslySetInnerHTML={{ __html: html }} />; return <Warpper dangerouslySetInnerHTML={{ __html: html }} />;
} };

View File

@@ -24,7 +24,7 @@ const CopyView: React.FC<{ command: ICommand; editorProps: IMarkdownEditor & Too
document.execCommand(`copy`); document.execCommand(`copy`);
window.getSelection()?.removeAllRanges(); window.getSelection()?.removeAllRanges();
toast.success(<div></div>); toast.success(<div></div>);
} };
return ( return (
<Button type="button" onClick={handleClick}> <Button type="button" onClick={handleClick}>
{props.command.icon} {props.command.icon}
@@ -38,8 +38,8 @@ export const copy: ICommand = {
button: (command, props, opts) => <CopyView command={command} editorProps={{ ...props, ...opts }} />, button: (command, props, opts) => <CopyView command={command} editorProps={{ ...props, ...opts }} />,
icon: ( icon: (
<svg fill="currentColor" viewBox="0 0 24 24" height="16" width="16"> <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="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="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> </svg>
), ),
}; };

View File

@@ -1,6 +1,6 @@
import MarkdownEditor, { getCommands } from '@uiw/react-markdown-editor'; import MarkdownEditor, { getCommands } from '@uiw/react-markdown-editor';
import { useContext } from 'react'; import { useContext } from 'react';
import { EditorView } from "@codemirror/view"; import { EditorView } from '@codemirror/view';
import styled from 'styled-components'; import styled from 'styled-components';
import { Preview } from './Preview'; import { Preview } from './Preview';
import { copy } from './copy'; import { copy } from './copy';
@@ -29,4 +29,4 @@ export const HomePage = () => {
/> />
</Warpper> </Warpper>
); );
} };

View File

@@ -26,7 +26,7 @@ const Select = styled.select`
const ThemeView: React.FC<{ command: ICommand; editorProps: IMarkdownEditor & ToolBarProps }> = (props) => { const ThemeView: React.FC<{ command: ICommand; editorProps: IMarkdownEditor & ToolBarProps }> = (props) => {
const { theme, setTheme } = useContext(Context); 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 ( return (
<Select value={theme} onChange={handleChange}> <Select value={theme} onChange={handleChange}>
<option value="abcdef">Abcdef Theme</option> <option value="abcdef">Abcdef Theme</option>
@@ -55,8 +55,8 @@ export const theme: ICommand = {
button: (command, props, opts) => <ThemeView command={command} editorProps={{ ...props, ...opts }} />, button: (command, props, opts) => <ThemeView command={command} editorProps={{ ...props, ...opts }} />,
icon: ( icon: (
<svg fill="currentColor" viewBox="0 0 24 24" height="16" width="16"> <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="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="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> </svg>
), ),
}; };

View File

@@ -1,4 +1,4 @@
import React from "react"; import React from 'react';
import { abcdef } from '@uiw/codemirror-theme-abcdef'; import { abcdef } from '@uiw/codemirror-theme-abcdef';
import { androidstudio } from '@uiw/codemirror-theme-androidstudio'; import { androidstudio } from '@uiw/codemirror-theme-androidstudio';
import { atomone } from '@uiw/codemirror-theme-atomone'; 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'; import { xcodeLight, xcodeDark } from '@uiw/codemirror-theme-xcode';
export const themes = { export const themes = {
abcdef, androidstudio, atomone, bbedit, bespin, darcula, dracula, duotoneLight, duotoneDark, eclipse, abcdef,
githubLight, githubDark, okaidia, sublime, xcodeLight, xcodeDark androidstudio,
} atomone,
bbedit,
bespin,
darcula,
dracula,
duotoneLight,
duotoneDark,
eclipse,
githubLight,
githubDark,
okaidia,
sublime,
xcodeLight,
xcodeDark,
};
export type ThemeValue = keyof typeof themes; export type ThemeValue = keyof typeof themes;
@@ -28,24 +42,26 @@ export interface CreateContext {
} }
export const Context = React.createContext<CreateContext>({ export const Context = React.createContext<CreateContext>({
css: "", css: '',
setCss: () => {}, setCss: () => {},
theme: 'githubLight', theme: 'githubLight',
setTheme: () => {}, setTheme: () => {},
}); });
export const Provider: React.FC<React.PropsWithChildren> = ({ children }) => { export const Provider: React.FC<React.PropsWithChildren> = ({ children }) => {
const [css, setCss] = React.useState(""); const [css, setCss] = React.useState('');
const [theme, setTheme] = React.useState<ThemeValue>("githubLight"); const [theme, setTheme] = React.useState<ThemeValue>('githubLight');
return ( return (
<Context.Provider <Context.Provider
value={{ value={{
css, setCss, css,
theme, setTheme, setCss,
theme,
setTheme,
}} }}
> >
{children} {children}
</Context.Provider> </Context.Provider>
); );
}; };

View File

@@ -4,25 +4,25 @@ export const getBlock = (data: any, str: string = '') => {
if (data && data.data && data.data.type === 'Declaration') { if (data && data.data && data.data.type === 'Declaration') {
str = `${data.data.property}: ${data.data.value.value}${data.data.important ? ' !important' : ''};`; str = `${data.data.property}: ${data.data.value.value}${data.data.important ? ' !important' : ''};`;
if (data.next) { if (data.next) {
str += getBlock(data.next) str += getBlock(data.next);
} }
} }
return str; return str;
} };
export const cssdata = (list: any, result: Record<string, string> = {}) => { export const cssdata = (list: any, result: Record<string, string> = {}) => {
if (list.data && list.data.type === 'Rule') { if (list.data && list.data.type === 'Rule') {
result[list.data.prelude.value] = getBlock(list.data.block.children.head); result[list.data.prelude.value] = getBlock(list.data.block.children.head);
if (list.next) { if (list.next) {
result = cssdata(list.next, {...result}) result = cssdata(list.next, { ...result });
} }
} }
return result; return result;
} };
export const spaceEscape = (node: RootContent) => { export const spaceEscape = (node: RootContent) => {
if (node.type === 'element' && node.children) { if (node.type === 'element' && node.children) {
const className = (node.properties?.className as string[]); const className = node.properties?.className as string[];
if (className) { if (className) {
if (!node.properties) { if (!node.properties) {
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.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) { if (elm.type === 'element' && elm.children) {
spaceEscape(elm) spaceEscape(elm);
} }
if (elm.type === 'text') { 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; type ChildContent = Element | Text;
const getNodeText = (node: ChildContent[]) => { const getNodeText = (node: ChildContent[]) => {
@@ -50,53 +50,64 @@ const getNodeText = (node: ChildContent[]) => {
else if (item.type === 'element') { else if (item.type === 'element') {
str += getNodeText(item.children as ChildContent[]); str += getNodeText(item.children as ChildContent[]);
} }
}) });
return str.replace(/↩/, ''); return str.replace(/↩/, '');
} };
export const footnotes = (node: Element) => { export const footnotes = (node: Element) => {
node.children.map((item) => { node.children.map((item) => {
if (item.type === 'element' && item.tagName === 'h2') { if (item.type === 'element' && item.tagName === 'h2') {
if (!item.properties) item.properties = {}; if (!item.properties) item.properties = {};
item.properties.className = ['footnotes-title']; item.properties.className = ['footnotes-title'];
item.children = [{ item.children = [
type: 'text', {
value: '参考' type: 'text',
}]; value: '参考',
},
];
} }
if (item.type === 'element' && item.tagName === 'ol') { if (item.type === 'element' && item.tagName === 'ol') {
item.children.map((li) => { item.children.map((li) => {
if (li.type === 'element' && li.tagName === 'li') { if (li.type === 'element' && li.tagName === 'li') {
if (!li.properties) li.properties = {}; if (!li.properties) li.properties = {};
li.properties.className = ['footnotes-list']; li.properties.className = ['footnotes-list'];
li.children = [{ li.children = [
type: 'text', {
value: getNodeText(li.children as ChildContent[]) type: 'text',
}] value: getNodeText(li.children as ChildContent[]),
},
];
} }
return li; return li;
}) });
} }
return item; return item;
}) });
} };
export const footnotesLabel = (node: Element) => { export const footnotesLabel = (node: Element) => {
const label = getNodeText(node.children as ChildContent[]); const label = getNodeText(node.children as ChildContent[]);
node.children = [{ node.children = [
type: 'text', {
value: `[${label}]` type: 'text',
}]; value: `[${label}]`,
} },
];
};
export const imagesStyle = (node: Element, parent: Root | Element | null) => { 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') { if (parent.tagName === 'p') {
parent.tagName = 'figure' parent.tagName = 'figure';
} }
if (!parent.properties) parent.properties = {} if (!parent.properties) parent.properties = {};
parent.properties.className = ['image-warpper'] parent.properties.className = ['image-warpper'];
if (!node.properties) node.properties = {} if (!node.properties) node.properties = {};
node.properties.className = ['image'] node.properties.className = ['image'];
} }
} };

View File

@@ -13,17 +13,15 @@ import rehypeRewrite from 'rehype-rewrite';
import stringify from 'rehype-stringify'; import stringify from 'rehype-stringify';
import { cssdata, spaceEscape, footnotes, footnotesLabel, imagesStyle } from './css'; import { cssdata, spaceEscape, footnotes, footnotesLabel, imagesStyle } from './css';
export type MarkdownToHTMLOptions = { export type MarkdownToHTMLOptions = {};
}
export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLOptions = {}) { export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLOptions = {}) {
const ast = csstree.parse(css, { const ast = csstree.parse(css, {
parseAtrulePrelude: false, parseAtrulePrelude: false,
parseRulePrelude: false, parseRulePrelude: false,
parseValue: false, parseValue: false,
parseCustomProperty: false, parseCustomProperty: false,
positions: false positions: false,
}); });
// @ts-ignore // @ts-ignore
const data = cssdata(ast.children.head); const data = cssdata(ast.children.head);
@@ -33,32 +31,46 @@ export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLO
.use(remarkRehype, { allowDangerousHtml: true }) .use(remarkRehype, { allowDangerousHtml: true })
.use(rehypePrism) .use(rehypePrism)
.use(rehypeRaw) .use(rehypeRaw)
.use(rehypeIgnore, { }) .use(rehypeIgnore, {})
.use(rehypeAttrs, { properties: 'attr' }) .use(rehypeAttrs, { properties: 'attr' })
.use(rehypeRewrite, { .use(rehypeRewrite, {
rewrite: (node, index, parent) => { rewrite: (node, index, parent) => {
// @ts-ignore // @ts-ignore
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName === 'pre') { if (
spaceEscape(node) 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')) { if (
footnotes(node) node?.type === 'element' &&
node.tagName === 'section' &&
(node?.properties?.className as string[]).includes('footnotes')
) {
footnotes(node);
} }
if (node?.type === 'element' && node.tagName === 'sup') { if (node?.type === 'element' && node.tagName === 'sup') {
footnotesLabel(node) footnotesLabel(node);
} }
if (node?.type === 'element' && node.tagName === 'img') { if (node?.type === 'element' && node.tagName === 'img') {
imagesStyle(node, parent) imagesStyle(node, parent);
} }
// Code Spans style // Code Spans style
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName !== 'pre') { if (
if (!node.properties) node.properties = {} node?.type === 'element' &&
node?.tagName === 'code' &&
parent?.type === 'element' &&
parent?.tagName !== 'pre'
) {
if (!node.properties) node.properties = {};
node.properties!.className = ['code-spans']; node.properties!.className = ['code-spans'];
} }
// List TODO style // 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') { 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; return;
} }
@@ -67,25 +79,25 @@ export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLO
if (!node.properties) { if (!node.properties) {
node.properties = {}; node.properties = {};
} }
const className = (node.properties?.className as string[]); const className = node.properties?.className as string[];
let style = ''; let style = '';
if (className) { if (className) {
className.forEach((name) => { className.forEach((name) => {
if (data[`.${name}`]) { if (data[`.${name}`]) {
style = data[`.${name}`]; style = data[`.${name}`];
} }
}) });
} }
if (!style) style = data[node.tagName]; if (!style) style = data[node.tagName];
if (style) { if (style) {
node.properties.style = style + (node.properties.style || ''); node.properties.style = style + (node.properties.style || '');
} }
} }
} },
}) })
.use(stringify); .use(stringify);
const file = new VFile(); const file = new VFile();
file.value = md; file.value = md;
const hastNode = processor.runSync(processor.parse(file), file); const hastNode = processor.runSync(processor.parse(file), file);
return String(processor.stringify(hastNode, file)); return String(processor.stringify(hastNode, file));
} }