新版 JSON Schema 初稿

This commit is contained in:
2betop 2020-09-08 17:39:14 +08:00
parent 17a90a0757
commit 084e70f911
23 changed files with 668 additions and 18 deletions

1
.gitignore vendored
View File

@ -21,3 +21,4 @@ node_modules
/toolkit/output
/coverage
/package-lock.json
/schema.json

View File

@ -4,7 +4,7 @@
const path = require('path');
const fs = require('fs');
const package = require('./package.json');
const parserMarkdown = require('./build/md-parser');
const parserMarkdown = require('./scripts/md-parser');
fis.get('project.ignore').push('public/**', 'gh-pages/**', '.*/**');
// 配置只编译哪些文件。
@ -35,7 +35,8 @@ fis.set('project.files', [
'/examples/*.tpl',
'/src/**.html',
'mock/**',
'schemas/**'
'schemas/**',
'/schema.json'
]);
fis.match('/mock/**', {
@ -414,7 +415,7 @@ if (fis.project.currentMedia() === 'publish') {
resourceType: 'mod'
}),
require('./build/embed-packager')
require('./scripts/embed-packager')
]
});

View File

@ -12,7 +12,8 @@
"publish2npm": "sh publish.sh && npm publish",
"build": "sh publish.sh",
"prettier": "prettier --write '{src,examples,scss}/**/*.{tsx,ts,jsx,scss}'",
"deploy-gh-page": "sh ./deploy-gh-pages.sh"
"deploy-gh-page": "sh ./deploy-gh-pages.sh",
"build-schemas": "ts-node -O '{\"target\":\"es6\"}' scripts/build-schemas.ts"
},
"repository": {
"type": "git",
@ -87,6 +88,7 @@
"@types/jest": "^24.0.11",
"@types/jquery": "^3.3.1",
"@types/lodash": "^4.14.76",
"@types/mkdirp": "^1.0.1",
"@types/node": "^12.7.1",
"@types/pretty-bytes": "^4.0.0",
"@types/prop-types": "^15.5.2",
@ -136,6 +138,7 @@
"js-yaml": "^3.10.0",
"lint-staged": "^8.1.6",
"marked": "^0.3.7",
"mkdirp": "^1.0.4",
"mobx-wiretap": "^0.12.0",
"prettier": "^2.0.5",
"prismjs": "^1.20.0",
@ -145,7 +148,9 @@
"react-testing-library": "6.0.4",
"strip-json-comments": "^2.0.1",
"ts-jest": "^24.0.0",
"typescript": "^3.9.5"
"ts-json-schema-generator": "^0.73.0",
"ts-node": "^9.0.0",
"typescript": "^4.0.2"
},
"jest": {
"testEnvironment": "jsdom",

119
scripts/build-schemas.ts Normal file
View File

@ -0,0 +1,119 @@
/**
* @file json-schemas
*/
import fs = require('fs');
import path = require('path');
import tsj = require('ts-json-schema-generator');
import mkdirp = require('mkdirp');
import {
ChainTypeFormatter,
CircularReferenceTypeFormatter,
AnnotatedTypeFormatter,
StringTypeFormatter,
NumberTypeFormatter,
BooleanTypeFormatter,
NullTypeFormatter,
AnyTypeFormatter,
UndefinedTypeFormatter,
UnknownTypeFormatter,
LiteralTypeFormatter,
EnumTypeFormatter,
ReferenceTypeFormatter,
Config,
DefinitionTypeFormatter,
ObjectTypeFormatter,
AliasTypeFormatter,
PrimitiveUnionTypeFormatter,
LiteralUnionTypeFormatter,
ArrayTypeFormatter,
TupleTypeFormatter,
IntersectionTypeFormatter
} from 'ts-json-schema-generator';
import {OptionalTypeFormatter} from './schema-tools/OptionalTypeFormatter';
import {VoidTypeFormatter} from './schema-tools/VoidTypeFormatter';
import {RestTypeFormatter} from './schema-tools/RestTypeFormatter';
import {UnionTypeFormatter} from './schema-tools/UnionTypeFormatter';
function createFormatter(config: Config) {
const chainTypeFormatter = new ChainTypeFormatter([]);
const circularReferenceTypeFormatter = new CircularReferenceTypeFormatter(
chainTypeFormatter
);
chainTypeFormatter
.addTypeFormatter(
new AnnotatedTypeFormatter(circularReferenceTypeFormatter)
)
.addTypeFormatter(new StringTypeFormatter())
.addTypeFormatter(new NumberTypeFormatter())
.addTypeFormatter(new BooleanTypeFormatter())
.addTypeFormatter(new NullTypeFormatter())
.addTypeFormatter(new AnyTypeFormatter())
.addTypeFormatter(new UndefinedTypeFormatter())
.addTypeFormatter(new UnknownTypeFormatter())
.addTypeFormatter(new VoidTypeFormatter())
.addTypeFormatter(new LiteralTypeFormatter())
.addTypeFormatter(new EnumTypeFormatter())
.addTypeFormatter(
new ReferenceTypeFormatter(
circularReferenceTypeFormatter,
config.encodeRefs ?? true
)
)
.addTypeFormatter(
new DefinitionTypeFormatter(
circularReferenceTypeFormatter,
config.encodeRefs ?? true
)
)
.addTypeFormatter(new ObjectTypeFormatter(circularReferenceTypeFormatter))
.addTypeFormatter(new AliasTypeFormatter(circularReferenceTypeFormatter))
.addTypeFormatter(new PrimitiveUnionTypeFormatter())
.addTypeFormatter(new LiteralUnionTypeFormatter())
.addTypeFormatter(new OptionalTypeFormatter(circularReferenceTypeFormatter))
.addTypeFormatter(new RestTypeFormatter(circularReferenceTypeFormatter))
.addTypeFormatter(new ArrayTypeFormatter(circularReferenceTypeFormatter))
.addTypeFormatter(new TupleTypeFormatter(circularReferenceTypeFormatter))
.addTypeFormatter(new UnionTypeFormatter(circularReferenceTypeFormatter))
.addTypeFormatter(
new IntersectionTypeFormatter(circularReferenceTypeFormatter)
);
return circularReferenceTypeFormatter;
}
/**
*
*/
async function main() {
const dir = path.join(__dirname, '../src/schemas');
const outDir = path.join(__dirname, '../');
const tsConfig = path.join(__dirname, '../tsconfig.json');
const config = {
path: path.join(dir, 'index.ts'),
tsconfig: tsConfig,
type: 'PageSchema'
};
const program = tsj.createProgram(config);
const parser = tsj.createParser(program, config);
const formatter = createFormatter(config);
const generator = new tsj.SchemaGenerator(program, parser, formatter, config);
const schema = generator.createSchema(config.type);
const outputFile = path.join(outDir, 'schema.json');
mkdirp(path.dirname(outputFile));
fs.writeFileSync(outputFile, JSON.stringify(schema, null, 2));
}
main().catch(e => console.error(e));

View File

@ -0,0 +1,34 @@
import {
SubTypeFormatter,
TypeFormatter,
BaseType,
Definition
} from 'ts-json-schema-generator';
export class OptionalType extends BaseType {
public constructor(private item: BaseType) {
super();
}
public getId(): string {
return this.item.getId() + '?';
}
public getType(): BaseType {
return this.item;
}
}
export class OptionalTypeFormatter implements SubTypeFormatter {
public constructor(private childTypeFormatter: TypeFormatter) {}
public supportsType(type: OptionalType): boolean {
return type instanceof OptionalType;
}
public getDefinition(type: OptionalType): Definition {
return this.childTypeFormatter.getDefinition(type.getType());
}
public getChildren(type: OptionalType): BaseType[] {
return this.childTypeFormatter.getChildren(type.getType());
}
}

View File

@ -0,0 +1,35 @@
import {
SubTypeFormatter,
TypeFormatter,
Definition,
BaseType,
ArrayType
} from 'ts-json-schema-generator';
export class RestType extends BaseType {
public constructor(private item: ArrayType) {
super();
}
public getId(): string {
return '...' + this.item.getId();
}
public getType(): ArrayType {
return this.item;
}
}
export class RestTypeFormatter implements SubTypeFormatter {
public constructor(private childTypeFormatter: TypeFormatter) {}
public supportsType(type: RestType): boolean {
return type instanceof RestType;
}
public getDefinition(type: RestType): Definition {
return this.childTypeFormatter.getDefinition(type.getType());
}
public getChildren(type: RestType): BaseType[] {
return this.childTypeFormatter.getChildren(type.getType());
}
}

View File

@ -0,0 +1,19 @@
import {
UnionTypeFormatter as BaseUnionTypeFormatter,
UnionType,
Definition,
TypeFormatter
} from 'ts-json-schema-generator';
export class UnionTypeFormatter extends BaseUnionTypeFormatter {
public constructor(protected _childTypeFormatter: TypeFormatter) {
super(_childTypeFormatter);
}
getDefinition(type: UnionType): Definition {
// toDo
// 目前这块生成的 anyOf
// 既然 json-schema-draft-07 已经支持 if else 了,改成那样应该更加精准。
return super.getDefinition(type);
}
}

View File

@ -0,0 +1,19 @@
import {BaseType, SubTypeFormatter, Definition} from 'ts-json-schema-generator';
export class VoidType extends BaseType {
public getId(): string {
return 'void';
}
}
export class VoidTypeFormatter implements SubTypeFormatter {
public supportsType(type: VoidType): boolean {
return type instanceof VoidType;
}
public getDefinition(type: VoidType): Definition {
return {type: 'null'};
}
public getChildren(type: VoidType): BaseType[] {
return [];
}
}

View File

@ -4,13 +4,10 @@ import {filter} from '../utils/tpl';
import cx from 'classnames';
import {anyChanged} from '../utils/helper';
import {escapeHtml} from '../utils/tpl-builtin';
import {TplSchema} from '../schemas/Tpl';
export interface TplProps extends RendererProps {
export interface TplProps extends RendererProps, TplSchema {
className?: string;
tpl?: string;
html?: string;
text?: string;
raw?: string;
value?: string;
wrapperComponent?: any;
inline?: boolean;

104
src/schemas/Action.ts Normal file
View File

@ -0,0 +1,104 @@
import {
SchemaClassName,
SchemaExpression,
SchemaApi,
SchemaReload,
SchemaTpl
} from './Schema';
export type ButtonSchema = {
block?: boolean;
className?: SchemaClassName;
disabled?: boolean;
disabledOn?: SchemaExpression;
hidden?: boolean;
hiddenOn?: SchemaExpression;
icon?: string;
iconClassName?: SchemaClassName;
label?: string;
level?: 'info' | 'success' | 'warning' | 'danger' | 'link' | 'primary';
primary?: boolean;
size?: 'xs' | 'sm' | 'md' | 'lg';
tooltip?: string;
tooltipPlacement?: 'top' | 'right' | 'bottom' | 'left';
type: 'button' | 'submit' | 'reset';
visible?: boolean;
visibleOn?: SchemaExpression;
confirmText?: string;
};
export interface AjaxActionSchema extends ButtonSchema {
actionType: 'ajax';
api: SchemaApi;
// todo
feedback?: any;
reload?: SchemaReload;
}
export interface UrlActionSchema extends ButtonSchema {
actionType: 'url';
blank?: boolean;
url: string;
}
export interface DialogActionSchema extends ButtonSchema {
actionType: 'dialog';
// todo
dialog: any;
nextCondition?: SchemaExpression;
reload?: SchemaReload;
}
export interface DrawerActionSchema extends ButtonSchema {
actionType: 'drawer';
// todo
drawer: any;
nextCondition?: SchemaExpression;
reload?: SchemaReload;
}
export interface CopyActionSchema extends ButtonSchema {
actionType: 'copy';
copy: SchemaTpl;
}
export interface LinkActionSchema extends ButtonSchema {
actionType: 'link';
link: string;
}
export interface ReloadActionSchema extends ButtonSchema {
actionType: 'reload';
target: SchemaReload;
}
export interface OtherActionSchema extends ButtonSchema {
actionType: 'prev' | 'next' | 'cancel' | 'close' | 'submit' | 'confirm';
[propName: string]: any;
}
export type ActionSchema =
| AjaxActionSchema
| UrlActionSchema
| LinkActionSchema
| DialogActionSchema
| DrawerActionSchema
| CopyActionSchema
| ReloadActionSchema
| OtherActionSchema;

View File

@ -0,0 +1,27 @@
export interface FormBaseControl {
size?: 'xs' | 'sm' | 'md' | 'lg';
label?: string;
name?: string;
// todo
remark?: string;
// todo
labelRemark?: string;
/**
*
*/
hint?: string;
/**
*
*/
submitOnChange?: boolean;
readOnly?: boolean;
disabled?: boolean;
}

21
src/schemas/Form/Text.ts Normal file
View File

@ -0,0 +1,21 @@
import {FormBaseControl} from './BaseItem';
import {ActionSchema} from '../Action';
export interface TextControlSchema extends FormBaseControl {
type: 'text' | 'email' | 'password' | 'url';
/**
*
*/
trimContents?: string;
addOn?: (
| ActionSchema
| {
type: 'text';
label: string;
}
) & {
position?: 'left' | 'right';
};
}

30
src/schemas/Form/index.ts Normal file
View File

@ -0,0 +1,30 @@
import {ActionSchema} from '../Action';
import {SchemaDefaultData, SchemaApi} from '../Schema';
// todo
export type FormControlSchema = any;
/**
* amis Form https://baidu.gitee.io/amis/docs/components/form/index
*/
export interface FormSchema {
type: 'form';
title?: string;
actions?: Array<ActionSchema>;
controls?: Array<FormControlSchema>;
data?: SchemaDefaultData;
debug?: boolean;
/**
* Form api
*/
api?: SchemaApi;
/**
*
*/
initApi?: SchemaApi;
}

101
src/schemas/Page.ts Normal file
View File

@ -0,0 +1,101 @@
import {
SchemaContainer,
SchemaClassName,
SchemaApi,
SchemaExpression,
SchemaName,
SchemaDefaultData,
SchemaSchema,
BaseSchema
} from './Schema';
/**
* amis Page https://baidu.gitee.io/amis/docs/components/page
*/
export interface PageSchema extends BaseSchema {
/**
* page
*/
type: 'page';
/**
*
*/
title?: string;
/**
*
*/
subTitle?: string;
/**
* ,
*/
remark?: string; // todo
/**
*
*/
body?: SchemaContainer;
/**
* css
*/
bodyClassName?: SchemaClassName;
/**
*
*/
aside?: SchemaContainer;
/**
* css
*/
asideClassName?: SchemaClassName;
/**
* className
*/
className?: SchemaClassName;
data?: SchemaDefaultData;
/**
* header className
*/
headerClassName?: SchemaClassName;
/**
* API data data 使
*/
initApi?: SchemaApi;
/**
*
*/
initFetch?: boolean;
/**
*
*/
initFetchOn?: SchemaExpression;
messages?: {
fetchFailed?: string;
fetchSuccess?: string;
};
name?: SchemaName;
/**
* title
*/
toolbar?: SchemaContainer;
/**
* toolbar className
*/
toolbarClassName?: SchemaClassName;
definitions?: any;
}

0
src/schemas/Remark.ts Normal file
View File

123
src/schemas/Schema.ts Normal file
View File

@ -0,0 +1,123 @@
import {PageSchema} from './Page';
import {FormSchema} from './Form';
import {TplSchema} from './Tpl';
// 每加个类型,这补充一下。
export type SchemaType = 'page' | 'form' | 'tpl' | 'html';
export type SchemaObject = PageSchema | FormSchema | TplSchema;
export type SchemaContainer = SchemaObject | Array<SchemaObject> | SchemaTpl;
/**
* `data.xxx > 5`
*/
export type SchemaExpression = string;
// /**
// * css类名配置字符串或者对象。
// *
// * className: "red"
// *
// * 用对象配置时意味着你能跟表达式一起搭配使用,如:
// *
// * className: {
// * "red": "data.progress > 80",
// * "blue": "data.progress > 60"
// * }
// */
// export type SchemaClassName =
// | string
// | {
// [propName: string]: true | false | null | SchemaExpression;
// };
/**
* css类名
*/
export type SchemaClassName = string; // todo 支持上面那种格式。
export type SchemaApi =
| string
| {
/**
* API
*/
method?: 'get' | 'post' | 'put' | 'delete' | 'patch';
/**
* API
*/
url: string;
/**
* . key `&` `$$` , data . $$ key . $ , key .
*/
data?: {
[propName: string]: any;
};
/**
*
*/
dataType?: 'json' | 'form-data' | 'form';
/**
*
*/
responseType?: 'blob';
/**
* headers data
*/
headers?: {
[propName: string]: string;
};
/**
*
*/
sendOn?: SchemaExpression;
};
/**
*
*/
export type SchemaName = string;
/**
*
*
* name
*
* windows
*
* `foo?a=${a}&b=${b},boo?c=${c}`
*/
export type SchemaReload = string;
/**
*
*
* 1. `${xxx}` `${xxx|upperCase}`
* 2. `<%= data.xxx %>`
*
*
* https://baidu.gitee.io/amis/docs/concepts/template
*/
export type SchemaTpl = string;
/**
* 使
*/
export type SchemaDefaultData = object;
/**
* json schema
*/
export type SchemaSchema = string;
export interface BaseSchema {
$schema?: SchemaSchema;
type: SchemaType;
className?: SchemaClassName;
}

18
src/schemas/Tpl.ts Normal file
View File

@ -0,0 +1,18 @@
import {SchemaSchema, SchemaTpl, BaseSchema} from './Schema';
/**
* tpl
*/
export interface TplSchema extends BaseSchema {
/**
*
*
* https://baidu.gitee.io/amis/docs/concepts/template
*/
type: 'tpl' | 'html';
tpl?: SchemaTpl;
html?: SchemaTpl;
text?: SchemaTpl;
raw?: string;
}

3
src/schemas/index.ts Normal file
View File

@ -0,0 +1,3 @@
import {PageSchema} from './Page';
export default PageSchema;

View File

@ -22,13 +22,6 @@
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": [
"node_modules",
"build",
"scripts",
"acceptance-tests",
"webpack",
"jest"
],
"exclude": ["node_modules", "acceptance-tests", "webpack", "jest"],
"types": ["typePatches"]
}