Skip to content

Commit

Permalink
feat: add a tools page
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanyxh committed Aug 3, 2024
1 parent c307679 commit a909a72
Show file tree
Hide file tree
Showing 14 changed files with 986 additions and 530 deletions.
2 changes: 1 addition & 1 deletion .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pnpm run lint:fix
pnpm run stylelint:fix
# pnpm run stylelint:fix
pnpm run commitlint
pnpm run clean
27 changes: 24 additions & 3 deletions helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface RoutePlace {
books: string;
articles: string;
examples: string;
tools: string;
}

export const root = process.cwd();
Expand Down Expand Up @@ -186,18 +187,38 @@ export const generateRouteJSONWithExample = async () => {
return { examples: result };
};

export const generateRouteJSONWithTools = async () => {
let tools = '';

const dirs = readdirSync(resolve('./src/tools'));

for (let i = 0; i < dirs.length; i++) {
const name = dirs[i];

tools += `{
path: '${name + '.html'}',
element: () => import('@/tools/${name}/Index.tsx')
},
`;
}

return { tools };
};

export const generateRouteJSON = async () => {
return {
...(await generateRouteJSONWithArticle()),
...(await generateRouteJSONWithExample())
...(await generateRouteJSONWithExample()),
...(await generateRouteJSONWithTools())
};
};

export const replacePlaceRoute = (code: string, { books, articles, examples }: RoutePlace) =>
export const replacePlaceRoute = (code: string, { books, articles, examples, tools }: RoutePlace) =>
code
.replace('/** placeholder for articles */', articles)
.replace('/** placeholder for books */', books)
.replace('/** placeholder for coder */', examples);
.replace('/** placeholder for coder */', examples)
.replace('/** placeholder for tools */', tools);

export function resolveFullRoutes(
routes: ResolveRouteObject[],
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@milkdown/preset-commonmark": "^7.3.6",
"@milkdown/preset-gfm": "^7.3.6",
"@milkdown/utils": "^7.3.6",
"@monaco-editor/react": "^4.6.0",
"antd": "^5.17.2",
"classnames": "^2.5.1",
"lodash-es": "^4.17.21",
Expand Down
1,098 changes: 573 additions & 525 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/router/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorState> {
};

static getDerivedStateFromError(error: Error) {
console.error(error);
// Update state so the next render will show the fallback UI.
return { hasError: true, error: error };
}
Expand Down
7 changes: 7 additions & 0 deletions src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ export const routes: RouteObject[] = [
path: 'examples',
element: () => import('@/viewer/Examples')
},
{
path: 'tools',
element: () => import('@/viewer/Tools'),
children: [
/** placeholder for tools */
]
},
{
path: '404.html',
element: '不存在的页面'
Expand Down
25 changes: 25 additions & 0 deletions src/tools/json2typescript/Index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.container {
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}

.json,
.typescript {
flex: 1;
height: 100%;
}

.json {
border-inline: 1px solid #d9d9d9;
}

.toolbar {
margin-bottom: 12px;
padding-left: 20px;
height: 48px;
line-height: 48px;
font-size: 18px;
border-bottom: 1px solid #d9d9d9;
}
70 changes: 70 additions & 0 deletions src/tools/json2typescript/Index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useState } from 'react';

import style from './Index.module.less';
import { createType, generate } from './JSON2TypeScript';

import Editor from '@monaco-editor/react';

// const transform = (obj: any) => {
// const type = typeof obj;

// switch (type) {
// case 'string':
// case 'number':
// case 'bigint':
// case 'boolean':
// case 'object':
// }
// }

const json2ts = (json: string) => {
try {
const obj = JSON.parse(json);

console.log(createType(obj));

return generate(createType(obj));
} catch (err) {
console.log(err);

return void 0;
}
};

const JSON2TypeScript: React.FC = () => {
const [typescript, setTypeScript] = useState('');

const handleChange = (e: string | undefined) => {
const ts = json2ts(e || '');

if (typeof ts === 'string') {
setTypeScript(ts);
}
};

return (
<div className={style.container}>
<div className={style.json}>
<div className={style.toolbar}>JSON</div>
<Editor
height={'calc(100% - 60px)'}
defaultLanguage="json"
options={{ minimap: { enabled: false } }}
onChange={handleChange}
/>
</div>

<div className={style.typescript}>
<div className={style.toolbar}>TypeScript</div>
<Editor
height={'calc(100% - 60px)'}
defaultLanguage="typescript"
options={{ minimap: { enabled: false } }}
value={typescript}
/>
</div>
</div>
);
};

export default JSON2TypeScript;
202 changes: 202 additions & 0 deletions src/tools/json2typescript/JSON2TypeScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { isArray, isBoolean, isNull, isNumber, isObject, isString, upperFirst } from 'lodash-es';

type type = 'string' | 'number' | 'boolean' | 'null' | Record<string, Type> | Type[];

type Type = {
type: type;
require: boolean;
};

const createStringType = (): Type => {
return { type: 'string', require: true };
};

const createNumberType = (): Type => {
return { type: 'number', require: true };
};

const createBooleanType = (): Type => {
return { type: 'boolean', require: true };
};

const createNullType = (): Type => {
return { type: 'null', require: true };
};

const createObjectType = (data: Record<string, any>): Type => {
const keys = Object.keys(data);

const companion: Record<string, Type> = {};

for (let i = 0; i < keys.length; i++) {
const key = keys[i];

companion[key] = createType(data[key]);
}

return { type: companion, require: true };
};

const createArrayType = (data: any[]): Type => {
const companion: Type[] = [];

for (let i = 0; i < data.length; i++) {
const curr = data[i];

companion.push(createType(curr));
}

return { type: companion, require: true };
};

export function createType(data: any) {
let type!: Type;

switch (true) {
case isString(data):
type = createStringType();
break;
case isNumber(data):
type = createNumberType();
break;
case isNull(data):
type = createNullType();
break;
case isBoolean(data):
type = createBooleanType();
break;
case isArray(data):
type = createArrayType(data);
break;
case isObject(data):
type = createObjectType(data);
break;
}

return type;
}

export function generate(tree: Type) {
const typeStr: string[] = [];

const names = new Map<string, number>();

const generateString = () => 'string;\n';
const generateNumber = () => 'number;\n';
const generateBoolean = () => 'boolean;\n';
const generateNull = () => 'null;\n';

function getName(key: string) {
let typeName = key;

if (!Number.isNaN(window.parseInt(typeName))) {
const name = 'N' + typeName;

let count = names.get(name);

if (typeof count === 'number') {
typeName = name.padStart(name.length + ++count, 'N');

names.set(name, count);
} else {
typeName = name;

names.set(name, 0);
}
} else {
let count = names.get(typeName);

if (typeof count === 'number') {
count++;

typeName += count;

names.set(typeName, count);
} else {
names.set(typeName, 0);
}
}

typeName = upperFirst(typeName);

return typeName;
}

function generateObject(data: Record<string, Type>, name?: string) {
let type = '{\n';

const keys = Object.keys(data);

for (let i = 0; i < keys.length; i++) {
const key = keys[i] as keyof typeof data;
const prop = data[key];

const typeName = getName(key);

switch (prop.type) {
case 'string':
type += ` ${key}: ${generateString()}`;
break;
case 'number':
type += ` ${key}: ${generateNumber()}`;
break;
case 'null':
type += ` ${key}: ${generateNull()}`;
break;
case 'boolean':
type += ` ${key}: ${generateBoolean()}`;
break;
default:
if (isArray(prop.type)) {
generateArray(prop.type, typeName);

type += ` ${key}: ${typeName};\n`;
} else if (isObject(prop.type)) {
generateObject(prop.type, typeName);

type += ` ${key}: ${typeName};\n`;
}
}
}

if (!name) {
return typeStr.push(`export interface Root ${type}}`);
}

typeStr.push(`export interface ${name} ${type}}`);
}

function generateArray(data: Type[], name?: string) {
let type = '';

if (data.length === 0) {
type += 'any[];\n';
} else if (data.every((e) => e.type === 'string')) {
type += 'string[];\n';
} else if (data.every((e) => e.type === 'number')) {
type += 'string[];\n';
} else if (data.every((e) => e.type === 'boolean')) {
type += 'boolean[];\n';
} else if (data.every((e) => e.type === 'null')) {
type += 'null[];\n';
} else {
// for (let i = 0; i < data.length; i++) {
// const element = data[i];
// }
}

if (!name) {
return `export type Root = ` + type;
}

typeStr.push(`type ${name} = ${type}[];\n`);
}

if (isArray(tree.type)) {
generateArray(tree.type);
} else if (isObject(tree.type)) {
generateObject(tree.type);
}

return typeStr.reverse().join('\n\n');
}
Loading

0 comments on commit a909a72

Please sign in to comment.