Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

增加ts的支持 #15

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# egg-graphql

---

[GraphQL](http://facebook.github.io/graphql/)使用 Schema 来描述数据,并通过制定和实现 GraphQL 规范定义了支持 Schema 查询的 DSQL (Domain Specific Query Language,领域特定查询语言,由 FACEBOOK 提出。
Expand Down Expand Up @@ -62,15 +63,15 @@ exports.graphql = {
// 是否加载开发者工具 graphiql, 默认开启。路由同 router 字段。使用浏览器打开该可见。
graphiql: true,
// graphQL 路由前的拦截器
onPreGraphQL: function* (ctx) {},
onPreGraphQL: function*(ctx) {},
// 开发工具 graphiQL 路由前的拦截器,建议用于做权限操作(如只提供开发者使用)
onPreGraphiQL: function* (ctx) {},
onPreGraphiQL: function*(ctx) {},
};
```

## 使用方式

请将 graphql 相关逻辑放到 app/graphql 下,请参考测试用例,里面有connector/schema 的目录结构, 以及 dataloader 的使用。
请将 graphql 相关逻辑放到 app/graphql 下,请参考测试用例,里面有 connector/schema 的目录结构, 以及 dataloader 的使用。

目录结构如下

Expand All @@ -85,21 +86,30 @@ exports.graphql = {
│   │   └── user // 一个graphql模型
│   │   ├── connector.js
│   │   ├── resolver.js
│   │   └── schema.graphql
│   │   └── schema.graphql
│   ├── model
│   │   └── user.js
│   ├── public
│   └── router.js
```

## ts 的支持

在项目根目录创建 tshelper.js,并赋值如下代码:

```
module.exports = require("egg-graphql/tshelper");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉这个不应该插件来做

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是的,看到egg-ts-helper源码中定义了默加载路径,只是提供了对外的配置,没有想到pull-request

```

## 增加了 schemaDireactives 的支持

## 参考文章

- [graphql官网](http://facebook.github.io/graphql)
- [graphql 官网](http://facebook.github.io/graphql)

- [如何在egg中使用graphql](https://zhuanlan.zhihu.com/p/30604868)
- [如何在 egg 中使用 graphql](https://zhuanlan.zhihu.com/p/30604868)

- [项目例子:结合sequelize](https://github.com/freebyron/egg-graphql-boilerplate)
- [项目例子:结合 sequelize](https://github.com/freebyron/egg-graphql-boilerplate)

## 协议

Expand Down
4 changes: 1 addition & 3 deletions agent.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict';

module.exports = agent => {
require('./lib/load_schema')(agent);
require('./lib/load_connector')(agent);
require('./lib/loader/graphql-loader')(agent);
};

4 changes: 1 addition & 3 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict';

module.exports = app => {
require('./lib/load_schema')(app);
require('./lib/load_connector')(app);
require('./lib/loader/graphql-loader')(app);
};

1 change: 0 additions & 1 deletion app/extend/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const SYMBOL_CONNECTOR = Symbol('connector');

module.exports = {

/**
* connector instance
* @member Context#connector
Expand Down
31 changes: 0 additions & 31 deletions lib/load_connector.js

This file was deleted.

58 changes: 0 additions & 58 deletions lib/load_schema.js

This file was deleted.

110 changes: 110 additions & 0 deletions lib/loader/graphql-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use strict';

const { join, dirname } = require('path');
const { merge } = require('lodash');
const is = require('is-type-of');
const {
makeExecutableSchema,
SchemaDirectiveVisitor,
} = require('graphql-tools');

const SYMBOL_SCHEMA = Symbol('Application#schema');
const SYMBOL_CONNECTOR_CLASS = Symbol('Application#connectorClass');

module.exports = app => {
const directiveResolvers = {};
const schemaDirectives = {};
const resolvers = {};
const typeDefs = [];

class GraphqlLoader {
constructor(app) {
this.app = app;
}

load() {
const connectorClasses = new Map();
this.loadGraphql(connectorClasses);
this.loadTypeDefs();
/**
* create a GraphQL.js GraphQLSchema instance
*/
Object.defineProperties(this.app, {
schema: {
get() {
if (!this[SYMBOL_SCHEMA]) {
this[SYMBOL_SCHEMA] = makeExecutableSchema({
typeDefs,
resolvers,
directiveResolvers,
schemaDirectives,
});
}
return this[SYMBOL_SCHEMA];
},
},
connectorClass: {
get() {
if (!this[SYMBOL_CONNECTOR_CLASS]) {
this[SYMBOL_CONNECTOR_CLASS] = connectorClasses;
}
return this[SYMBOL_CONNECTOR_CLASS];
},
},
});
}
// 加载graphql
loadGraphql(connectorClasses) {
const loader = this.app.loader;
loader.timing.start('Loader Graphql');
const opt = {
caseStyle: 'lower',
directory: join(this.app.baseDir, 'app/graphql'),
target: {},
initializer: (obj, opt) => {
const pathName = opt.pathName.split('.').pop();
// 加载resolver
if (pathName === 'resolver') {
merge(resolvers, obj);
}
// 加载schemaDirective
if (is.class(obj)) {
const proto = Object.getPrototypeOf(obj);
if (proto === SchemaDirectiveVisitor) {
const name = opt.pathName.split('.').pop();
schemaDirectives[name] = obj;
}
}
// 加载directiveResolver
if (pathName === 'directive') {
merge(directiveResolvers, obj);
}
// 加载connector
if (pathName === 'connector') {
// 获取文件目录名
const type = dirname(opt.path)
.split(/\/|\\/)
.pop();
connectorClasses.set(type, obj);
}
},
};
new this.app.loader.FileLoader(opt).load();
loader.timing.end('Loader Graphql');
}
// 加载typeDefs
loadTypeDefs() {
const opt = {
directory: join(this.app.baseDir, 'app/graphql'),
match: '**/*.graphql',
target: {},
initializer: obj => {
typeDefs.push(obj.toString('utf8'));
},
};
new this.app.loader.FileLoader(opt).load();
}
}

new GraphqlLoader(app).load();
};
75 changes: 75 additions & 0 deletions lib/ts-generator/connector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict';

const utils = require('egg-ts-helper/dist/utils');
const d = require('debug');
const path = require('path');

const debug = d('egg-ts-helper#generators_extend');
module.exports = (config, baseConfig) => {
const fileList = utils.loadFiles(config.dir, config.pattern);
const dist = path.resolve(baseConfig.typings, 'app/connector/index.d.ts');

debug('file list : %o', fileList);
if (!fileList.length) {
return { dist };
}

// using to compose import code
let importStr = '';
// using to create interface mapping
const interfaceMap = {};

fileList.forEach(f => {
f = f.substring(0, f.lastIndexOf('.'));
const obj = utils.getModuleObjByPath(f);
const tsPath = path
.relative(config.dtsDir, path.join(config.dir, f))
.replace(/\/|\\/g, '/');
debug('import %s from %s', obj.moduleName, tsPath);
importStr += `import ${obj.moduleName} from '${tsPath}';\n`;

// create mapping
const collector = interfaceMap;
if (obj.props.length) {
const name = utils.camelProp(
obj.props.shift(),
config.caseStyle || baseConfig.caseStyle
);
collector[name] = obj.moduleName;
}
});

// composing all the interface
const composeInterface = (obj, indent = '') => {
let str = '';

Object.keys(obj).forEach(key => {
const val = obj[key];
if (typeof val === 'string') {
str += `${indent + key}: ${
config.interfaceHandle ? config.interfaceHandle(val) : val
};\n`;
} else {
const newVal = composeInterface(val, indent + ' ');
if (newVal) {
str += `${indent + key}: {\n${newVal + indent}};\n`;
}
}
});

return str;
};

return {
dist,
content:
`${importStr}\n` +
`declare module '${config.framework || baseConfig.framework}' {\n` +
` interface ${config.interface} {\n` +
' connector: {\n' +
composeInterface(interfaceMap, ' ') +
' }\n' +
' }\n' +
'}\n',
};
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"agent.js",
"config",
"app",
"lib"
"lib",
"index.d.ts"
],
"ci": {
"version": "8, 9"
Expand Down
Loading