mirror of
https://github.com/wassname/talk.git
synced 2026-09-10 12:43:11 +08:00
embedded graph docs into static docs
This commit is contained in:
+3
-1
@@ -1,10 +1,12 @@
|
||||
**/*.html
|
||||
dist
|
||||
docs
|
||||
|
||||
node_modules
|
||||
plugins/*
|
||||
public
|
||||
|
||||
**/*.min.js
|
||||
|
||||
!plugins/talk-plugin-akismet
|
||||
!plugins/talk-plugin-auth
|
||||
!plugins/talk-plugin-author-menu
|
||||
|
||||
@@ -120,6 +120,12 @@ sidebar:
|
||||
url: /additional-plugins/
|
||||
- title: Plugin Recipes
|
||||
url: /plugin-recipes/
|
||||
- title: Reference
|
||||
children:
|
||||
- title: Server Plugin API
|
||||
url: /reference/server/
|
||||
- title: GraphQL API
|
||||
url: /reference/graphql/
|
||||
- title: FAQ
|
||||
children:
|
||||
- title: FAQ
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
"develop-theme": "nodemon -x 'rm db.json; hexo serve' -w assets/ -w code/ -w source/ -w themes/ -w scripts/"
|
||||
},
|
||||
"dependencies": {
|
||||
"cheerio": "^1.0.0-rc.2",
|
||||
"common-tags": "^1.7.2",
|
||||
"graphql-docs": "^0.2.0",
|
||||
"hexo": "^3.2.0",
|
||||
"hexo-generator-index": "^0.2.0",
|
||||
"hexo-renderer-marked": "^0.3.0",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Required Configuration
|
||||
permalink: /configuration/
|
||||
class: configuration
|
||||
toc: true
|
||||
---
|
||||
|
||||
Talk requires configuration in order to customize the installation. The default
|
||||
@@ -10,8 +11,8 @@ behavior is to load it's configuration from the environment, following the
|
||||
In development, you can specify configuration in a file named `.env` and it will
|
||||
be loaded into the environment when you run `yarn watch:server`.
|
||||
|
||||
The following variables do not have defaults, and are **required** to start your
|
||||
instance of Talk:
|
||||
The above variables do not have defaults, and are **required** to start your
|
||||
instance of Talk.
|
||||
|
||||
If you've already configured your application with the required configuration,
|
||||
you can further customize it's behavior by applying
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Advanced Configuration
|
||||
permalink: /advanced-configuration/
|
||||
class: configuration
|
||||
toc: true
|
||||
---
|
||||
|
||||
Talk requires configuration in order to customize the installation. The default
|
||||
@@ -10,8 +11,8 @@ behavior is to load its configuration from the environment, following the
|
||||
In development, you can specify configuration in a file named `.env` and it will
|
||||
be loaded into the environment when you run `yarn watch:server`.
|
||||
|
||||
The following variables have defaults, and are _optional_ to start your
|
||||
instance of Talk:
|
||||
The variables above have defaults, and are _optional_ to start your
|
||||
instance of Talk.
|
||||
|
||||
If this is your first time configuring Talk, ensure you've also added the
|
||||
[Required Configuration](./configuration) as well,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Default Plugins
|
||||
permalink: /default-plugins/
|
||||
class: configuration
|
||||
toc: true
|
||||
---
|
||||
|
||||
The default Talk plugins can be found in the `plugins.default.json` file
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Additional Plugins
|
||||
permalink: /additional-plugins/
|
||||
class: configuration
|
||||
toc: true
|
||||
---
|
||||
|
||||
Talk ships with several plugins that aren't enabled by default. These plugins
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Plugin Recipes
|
||||
permalink: /plugin-recipes/
|
||||
class: configuration
|
||||
toc: true
|
||||
---
|
||||
|
||||
Plugin Recipes are plugin templates used to help bootstrap the development of a
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
|
||||
<p><strong>Page not found :(</strong></p>
|
||||
<p>The requested page could not be found. Try searching above for your page.</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: GraphQL API
|
||||
permalink: /reference/graphql/
|
||||
---
|
||||
|
||||
We provide all services that Talk can provide via the GraphQL API documented
|
||||
below. For a primer about GraphQL, visit http://graphql.org/.
|
||||
|
||||
{% graphqldocs ../../client/coral-framework/graphql/introspection.json %}
|
||||
@@ -0,0 +1,521 @@
|
||||
---
|
||||
title: Server Plugin API
|
||||
permalink: /reference/server/
|
||||
toc: true
|
||||
class: configuration
|
||||
---
|
||||
|
||||
The server functionality of our plugin lives inside the `index.js` plugin folder
|
||||
that exports the configuration of our plugin.
|
||||
|
||||
my-plugin/
|
||||
├── client/
|
||||
│ └── ... <-- client side plugin files
|
||||
└── index.js <-- base + server plugin index
|
||||
|
||||
## Hooks
|
||||
|
||||
Each plugin should export a single object with all hooks available on it.
|
||||
|
||||
_**Note: You will have access to the whole core and other plugin's typeDefs,
|
||||
context, loaders, mutators, resolvers, hooks. This is intentional, as it
|
||||
encourages composing plugins to merge functionality, like a Slack plugin which
|
||||
provides a Slack notify context function as well as having the loader for
|
||||
comments.**_
|
||||
|
||||
The following are the hooks available:
|
||||
|
||||
### typeDefs
|
||||
|
||||
```graphql
|
||||
enum COLOUR {
|
||||
RED
|
||||
BLUE
|
||||
}
|
||||
|
||||
type Person {
|
||||
name: String!
|
||||
colour: COLOUR!
|
||||
}
|
||||
|
||||
type RootMutation {
|
||||
createPerson(name: String!): Person
|
||||
}
|
||||
|
||||
type RootQuery {
|
||||
people: [Person!]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
leader: Person
|
||||
}
|
||||
```
|
||||
|
||||
Thanks to [gql-merge](https://www.npmjs.com/package/gql-merge) the contents of
|
||||
`typeDefs` should be a string that will be _merged_ with the existing type
|
||||
definitions. `enum`'s will be appended to, types will be appended, and new types
|
||||
will be added.
|
||||
|
||||
### context
|
||||
|
||||
```js
|
||||
{
|
||||
Slack: (context) => ({
|
||||
notify: (message) => {
|
||||
// return a promise after we're done sending notifications.
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Any property provided here will be added to the context parameter available
|
||||
inside all resolvers, loaders, mutators, and of course, other context based
|
||||
plugins.
|
||||
|
||||
The top level item must accept a context for the request which it should use to
|
||||
configure the context plugin before it would be mounted at `context.plugins`.
|
||||
This plugin above would mount at: `context.plugins.Slack`, or, if you're using
|
||||
[object destructuring](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment), `{plugins: {Slack}}`.
|
||||
|
||||
### Sort
|
||||
|
||||
A special context hook, `Sort` will allow plugin authors to provide new
|
||||
methods to sort data. An example is as follows:
|
||||
|
||||
```js
|
||||
{
|
||||
Sort: () => ({
|
||||
Comments: { // <-- (1)
|
||||
likes: { // <-- (2)
|
||||
startCursor(ctx, nodes, {cursor}) { // <-- (3)
|
||||
return cursor != null ? cursor : 0;
|
||||
},
|
||||
endCursor(ctx, nodes, {cursor}) { // <-- (4)
|
||||
return nodes.length ? (cursor != null ? cursor : 0) + nodes.length : null;
|
||||
},
|
||||
sort(ctx, query, {cursor, sort}) { // <-- (5)
|
||||
if (cursor) {
|
||||
query = query.skip(cursor);
|
||||
}
|
||||
|
||||
return query.sort({
|
||||
'action_counts.like': sort === 'DESC' ? -1 : 1,
|
||||
created_at: sort === 'DESC' ? -1 : 1,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
This has a bunch of special features:
|
||||
|
||||
1. `Comments` is the name of the type being sorted, this is pluralized and
|
||||
capitalized.
|
||||
2. `likes` is the `sortBy` field in lowercase.
|
||||
3. `startCursor` will retrieve the start cursor based on the current set of
|
||||
nodes and the current cursor.
|
||||
4. `endCursor` will retrieve the end cursor based on the current set of nodes
|
||||
and the current cursor.
|
||||
5. `sort` will mutate the `query` to apply the sort operations.
|
||||
|
||||
All the `startCursor`, `endCursor`, and `sort` functions must be provided in
|
||||
order for the sorting to apply properly.
|
||||
|
||||
### loaders
|
||||
|
||||
```js
|
||||
(context) => ({
|
||||
People: {
|
||||
load: () => db.people.find({user: context.user})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Loaders should be provided as a function which returns a map which is used in
|
||||
the resolvers function. These must return a promise or a value.
|
||||
|
||||
### mutators
|
||||
|
||||
```js
|
||||
(context) => ({
|
||||
People: {
|
||||
create: (name) => {
|
||||
return db.people.insert({user: context.user, name});
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Mutators should be provided as a function which returns a map which is used in
|
||||
the resolvers function. These must return a promise or a value.
|
||||
|
||||
### resolvers
|
||||
|
||||
```js
|
||||
{
|
||||
Person: {
|
||||
name(obj, args, context) {
|
||||
return obj.name;
|
||||
},
|
||||
colour(obj, args, context) {
|
||||
// Bill likes the colour red, everyone else likes blue.
|
||||
return obj.name === 'bill' ? 'RED' : 'BLUE';
|
||||
}
|
||||
},
|
||||
RootQuery: {
|
||||
people(obj, args, {loaders: {People}}) {
|
||||
return People.load();
|
||||
}
|
||||
},
|
||||
RootMutation: {
|
||||
createPerson(obj, {name}, {mutators: {People}}) {
|
||||
return People.create(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Should return a resolver map as described in the
|
||||
[Apollo Docs](http://dev.apollodata.com/tools/graphql-tools/resolvers#Resolver-map).
|
||||
|
||||
This will merge with the existing resolvers in core and from previous plugins.
|
||||
|
||||
### hooks
|
||||
|
||||
```js
|
||||
{
|
||||
RootMutation: {
|
||||
createPerson: {
|
||||
post: async (obj, args, {plugins: {Slack}}, info, person) {
|
||||
if (!person) {
|
||||
return person;
|
||||
}
|
||||
|
||||
await Slack.notify(`A new person just was created with name ${person.name}`);
|
||||
|
||||
return person;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hooks here are pretty special, for each resolver field, you can specify a
|
||||
pre/post hook that will execute pre and post field resolution.
|
||||
|
||||
If your post function accepts four parameters, then it can modify the field
|
||||
result. It is *required* that the function resolves a promise (or returns) with
|
||||
the modified value or simply the original if you didn't modify it.
|
||||
|
||||
### setupFunctions
|
||||
|
||||
```js
|
||||
setupFunctions: {
|
||||
leader: (options, args) => ({
|
||||
leader: {
|
||||
filter: (person) => person.place === 1
|
||||
},
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
Setup functions allow you to create filters that control which pubsub.publish() events
|
||||
send data to the client. If the type in question contains args, clients may subscribe using those arguments to further filter their subscription.
|
||||
|
||||
For more information, see the [Apollo Docs](https://github.com/apollographql/graphql-subscriptions).
|
||||
|
||||
### tokenUserNotFound
|
||||
|
||||
```js
|
||||
tokenUserNotFound: async ({jwt, token}) => {
|
||||
let profile = await someExternalService(token);
|
||||
if (!profile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let user = await UserModel.findOneAndUpdate({
|
||||
id: profile.id
|
||||
}, {
|
||||
id: profile.id,
|
||||
username: profile.username,
|
||||
lowercaseUsername: profile.username.toLowerCase(),
|
||||
roles: [],
|
||||
profiles: []
|
||||
}, {
|
||||
setDefaultsOnInsert: true,
|
||||
new: true,
|
||||
upsert: true
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
```
|
||||
|
||||
The `tokenUserNotFound` hook allows auth integrations to hook into the event
|
||||
when a valid token is provided but a user can't be found in the database that
|
||||
matches the provided id.
|
||||
|
||||
The function is async, and should return the user object that was created in the
|
||||
database, or null if the user wasn't found. The `jwt` parameter of the object
|
||||
is the unpacked token, while `token` is the original jwt token string.
|
||||
|
||||
### tags
|
||||
|
||||
The tags hook allows a plugin to define tags that are code controlled (added
|
||||
or enabled by code). Below is an example pulled from the core off topic plugin
|
||||
on how to create a hook for the `OFF_TOPIC` name:
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
name: 'OFF_TOPIC',
|
||||
permissions: {
|
||||
public: true,
|
||||
self: true,
|
||||
roles: []
|
||||
},
|
||||
models: ['COMMENTS'],
|
||||
created_at: new Date()
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
You can refer to `models/schema/tag.js` for the available schema to match when
|
||||
creating models to enable/disable specific features.
|
||||
|
||||
### router
|
||||
|
||||
```js
|
||||
(router) => {
|
||||
router.get('/api/v1/people', (req, res) => {
|
||||
res.json({people: [{name: 'Bob'}]});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The Router hook allows you to create a function that accepts the base express
|
||||
router where you can mount any amount of middleware/routes to do any form of
|
||||
action needed by external applications.
|
||||
|
||||
### passport
|
||||
|
||||
```js
|
||||
const FacebookStrategy = require('passport-facebook').Strategy;
|
||||
const UsersService = require('services/users');
|
||||
const {ValidateUserLogin, HandleAuthPopupCallback} = require('services/passport');
|
||||
|
||||
module.exports = {
|
||||
passport(passport) {
|
||||
passport.use(new FacebookStrategy({
|
||||
clientID: process.env.TALK_FACEBOOK_APP_ID,
|
||||
clientSecret: process.env.TALK_FACEBOOK_APP_SECRET,
|
||||
callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`,
|
||||
passReqToCallback: true,
|
||||
profileFields: ['id', 'displayName', 'picture.type(large)']
|
||||
}, async (req, accessToken, refreshToken, profile, done) => {
|
||||
|
||||
let user;
|
||||
try {
|
||||
user = await UsersService.findOrCreateExternalUser(profile);
|
||||
} catch (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
return ValidateUserLogin(profile, user, done);
|
||||
}));
|
||||
},
|
||||
router(router) {
|
||||
|
||||
// Note that we have to import the passport instance here, it is
|
||||
// instantiated after all the strategies have been mounted.
|
||||
const {passport} = require('services/passport');
|
||||
|
||||
/**
|
||||
* Facebook auth endpoint, this will redirect the user immediately to facebook
|
||||
* for authorization.
|
||||
*/
|
||||
router.get('/facebook', passport.authenticate('facebook', {display: 'popup', authType: 'rerequest', scope: ['public_profile']}));
|
||||
|
||||
/**
|
||||
* Facebook callback endpoint, this will send the user a html page designed to
|
||||
* send back the user credentials upon successful login.
|
||||
*/
|
||||
router.get('/facebook/callback', (req, res, next) => {
|
||||
|
||||
// Perform the facebook login flow and pass the data back through the opener.
|
||||
passport.authenticate('facebook', HandleAuthPopupCallback(req, res, next))(req, res, next);
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### translations
|
||||
|
||||
```js
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
translations: path.join(__dirname, 'translations.yml'),
|
||||
};
|
||||
```
|
||||
|
||||
Where the `translations.yml` contains:
|
||||
|
||||
```yml
|
||||
en:
|
||||
embedlink:
|
||||
copy: "Copy Permalink"
|
||||
```
|
||||
|
||||
Which overrides the copy for the `embedlink.copy` template. You can
|
||||
also provide other languages as well by using the correct language
|
||||
prefix.
|
||||
|
||||
### websockets
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
websockets: {
|
||||
onConnect: (connectionParams, connection) => {
|
||||
// Do something with the connection params or connection, like
|
||||
// logging it out, or incrementing a metric.
|
||||
},
|
||||
onDisconnect: (connection) => {
|
||||
// Do something with the connection params or connection, like
|
||||
// logging it out, or decrementing a metric.
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This `websockets` hook can be used to attach methods to the
|
||||
`onConnect` and `onDisconnect` events on a server. The intention for
|
||||
this hook is to allow administrators instrument the active websocket
|
||||
connections.
|
||||
|
||||
### schemaLevelResolveFunction
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
schemaLevelResolveFunction: (root, args, ctx, info) => {
|
||||
// The GraphQL Operation Name. Example: CoralEmbedStream_Embed
|
||||
const name = info.operation.name !== null ? info.operation.name.value : null;
|
||||
// Maybe increment a metric based on the operation name...
|
||||
|
||||
// You must _always_ return the root.
|
||||
return root;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The `schemaLevelResolveFunction` provides a function that is attached
|
||||
at the schema level, so that all queries that are made will go through. This
|
||||
can be used to create a better view of the graph landscape by creating metrics
|
||||
of resolved query names.
|
||||
|
||||
## Full Example
|
||||
|
||||
Contents of `plugins.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": [
|
||||
"people"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Located in `plugins/people/index.js`:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
typeDefs: `
|
||||
enum COLOUR {
|
||||
RED
|
||||
BLUE
|
||||
}
|
||||
|
||||
type Person {
|
||||
name: String!
|
||||
colour: COLOUR!
|
||||
}
|
||||
|
||||
type RootMutation {
|
||||
createPerson(name: String!): Person
|
||||
}
|
||||
|
||||
type RootQuery {
|
||||
people: [Person!]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
leader: Person
|
||||
}
|
||||
`,
|
||||
context: {
|
||||
Slack: () => ({
|
||||
notify: (message) => {
|
||||
// return a promise after we're done sending notifications.
|
||||
}
|
||||
})
|
||||
},
|
||||
loaders: ({user}) => ({
|
||||
People: {
|
||||
load: () => db.people.find({user})
|
||||
}
|
||||
}),
|
||||
mutators: ({user}) => ({
|
||||
People: {
|
||||
create: (name) => {
|
||||
return db.people.insert({user, name});
|
||||
}
|
||||
}
|
||||
}),
|
||||
resolvers: {
|
||||
Person: {
|
||||
name(obj, args, context) {
|
||||
return obj.name;
|
||||
},
|
||||
colour(obj, args, context) {
|
||||
// Bill likes the colour red, everyone else likes blue.
|
||||
return obj.name === 'bill' ? 'RED' : 'BLUE';
|
||||
}
|
||||
},
|
||||
RootQuery: {
|
||||
people(obj, args, {loaders: {People}}) {
|
||||
return People.load();
|
||||
}
|
||||
},
|
||||
RootMutation: {
|
||||
createPerson(obj, {name}, {mutators: {People}}) {
|
||||
return People.create(name);
|
||||
}
|
||||
}
|
||||
},
|
||||
hooks: {
|
||||
RootMutation: {
|
||||
createPerson: {
|
||||
post: async (obj, args, {plugins: {Slack}}, info, person) => {
|
||||
if (!person) {
|
||||
return person;
|
||||
}
|
||||
|
||||
await Slack.notify(`A new person just was created with name ${person.name}`);
|
||||
|
||||
return person;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
setupFunctions: {
|
||||
leader: (options, args) => ({
|
||||
leader: {
|
||||
filter: (person) => person.place === 1
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
Vendored
+4
-1
@@ -10,7 +10,10 @@
|
||||
<main class="content" role="main">
|
||||
{{ partial('partial/header') }}
|
||||
|
||||
{{ body }}
|
||||
{% block content %}
|
||||
{{ body }}
|
||||
{% endblock %}
|
||||
|
||||
</main>
|
||||
|
||||
{{ partial('partial/footer') }}
|
||||
|
||||
Vendored
+15
@@ -1,7 +1,22 @@
|
||||
<article{% if page.class %} class="{{ page.class }}"{% endif %}>
|
||||
{% if page.title %}
|
||||
<a class="btn btn-sm btn-light float-right suggest-edits plain-link" href="https://github.com/coralproject/talk/edit/master/docs/source/{{ page.source }}" title="Suggest edits to this page">Suggest Edits</a>
|
||||
<h1>{{ page.title }}</h1>
|
||||
<hr/>
|
||||
{% endif %}
|
||||
|
||||
{% if page.toc === true %}
|
||||
<aside>
|
||||
<nav class="toc" markdown="1">
|
||||
{% if page.toc_title %}
|
||||
<header>
|
||||
<h4 class="nav__title">{{ page.toc_title }}</h4>
|
||||
</header>
|
||||
{% endif %}
|
||||
{{ oltoul(toc(page.content, {list_number: false})) }}
|
||||
</nav>
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
||||
{{ page.content }}
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js" integrity="sha256-Daf8GuI2eLKHJlOWLRR/zRy9Clqcj4TUSumbxYH9kGI=" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/docsearch.js/2.4.1/docsearch.min.js" integrity="sha256-/cR58XeT7FS/4QEWCHJj/TdJ4b377Sf9p0DRto6I6cI=" crossorigin="anonymous"></script>
|
||||
{{ js(['js/highlight.min.js', 'js/main.js']) }}
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id={{ site.google_analytics }}"></script>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id={{ config.google_analytics }}"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments)};
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/* global hexo */
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { stripIndent } = require('common-tags');
|
||||
const docs = fs.readFileSync(
|
||||
require.resolve('graphql-docs/dist/graphql-docs.min.js'),
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
|
||||
hexo.extend.tag.register('graphqldocs', args => {
|
||||
const filename = path.resolve(hexo.source_dir, args[0]);
|
||||
const introspectionQuery = fs.readFileSync(filename, { encoding: 'utf8' });
|
||||
|
||||
return stripIndent`
|
||||
<div id="graphql-docs"></div>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js" integrity="sha256-oj3q2t3QPvtdjo4M5gZfrAXyHEfTfvYdfRL2jA2ZfOY=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js" integrity="sha256-sqgMIZkGTh7B/tF2nSyXc+tGBYCsfWiTl2II167jrOQ=" crossorigin="anonymous"></script>
|
||||
<script>${docs}</script>
|
||||
<script>
|
||||
function fetcher() {
|
||||
return new Promise(function(resolve) {
|
||||
resolve({"data": ${introspectionQuery}});
|
||||
});
|
||||
}
|
||||
const rootElem = document.getElementById('graphql-docs');
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
GraphQLDocs.GraphQLDocs,
|
||||
{
|
||||
fetcher: fetcher,
|
||||
}),
|
||||
rootElem
|
||||
);
|
||||
</script>
|
||||
`;
|
||||
});
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
/* global hexo */
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
// This helper replaces the ol tags outputted by the base toc helper with ui
|
||||
// tags.
|
||||
hexo.extend.helper.register('oltoul', function(source) {
|
||||
const $ = cheerio.load(source);
|
||||
|
||||
// Sourced from https://stackoverflow.com/a/12679823
|
||||
$(
|
||||
$('ol')
|
||||
.get()
|
||||
.reverse()
|
||||
).each(function() {
|
||||
$(this).replaceWith($('<ul>' + $(this).html() + '</ul>'));
|
||||
});
|
||||
|
||||
return $.html();
|
||||
});
|
||||
+19
-6
@@ -15,6 +15,24 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#graphql-docs {
|
||||
& > div > div {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
@extend .coral-link;
|
||||
}
|
||||
}
|
||||
|
||||
.toc {
|
||||
a {
|
||||
border-bottom: none !important;
|
||||
@extend .coral-link;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
@@ -261,12 +279,7 @@ pre {
|
||||
}
|
||||
}
|
||||
|
||||
.toc {
|
||||
a {
|
||||
@extend .coral-link;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.content {
|
||||
article {
|
||||
|
||||
+113
-5
@@ -2,6 +2,10 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/node@*":
|
||||
version "9.4.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.6.tgz#d8176d864ee48753d053783e4e463aec86b8d82e"
|
||||
|
||||
JSONStream@^1.0.7:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea"
|
||||
@@ -207,6 +211,13 @@ aws4@^1.2.1, aws4@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
|
||||
|
||||
babel-runtime@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
dependencies:
|
||||
core-js "^2.4.0"
|
||||
regenerator-runtime "^0.11.0"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
@@ -419,6 +430,17 @@ cheerio@^0.20.0:
|
||||
optionalDependencies:
|
||||
jsdom "^7.0.2"
|
||||
|
||||
cheerio@^1.0.0-rc.2:
|
||||
version "1.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db"
|
||||
dependencies:
|
||||
css-select "~1.2.0"
|
||||
dom-serializer "~0.1.0"
|
||||
entities "~1.1.1"
|
||||
htmlparser2 "^3.9.1"
|
||||
lodash "^4.15.0"
|
||||
parse5 "^3.0.1"
|
||||
|
||||
chokidar@^1.5.2, chokidar@^1.6.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
|
||||
@@ -520,6 +542,12 @@ commander@^2.9.0:
|
||||
version "2.14.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa"
|
||||
|
||||
common-tags@^1.7.2:
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.7.2.tgz#24d9768c63d253a56ecff93845b44b4df1d52771"
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
|
||||
component-emitter@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
|
||||
@@ -578,6 +606,10 @@ core-js@^1.1.1:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
|
||||
core-js@^2.4.0:
|
||||
version "2.5.3"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
|
||||
|
||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
@@ -740,7 +772,7 @@ dom-serializer@0, dom-serializer@~0.1.0:
|
||||
domelementtype "~1.1.1"
|
||||
entities "~1.1.1"
|
||||
|
||||
domelementtype@1:
|
||||
domelementtype@1, domelementtype@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
|
||||
|
||||
@@ -754,6 +786,12 @@ domhandler@2.3:
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
|
||||
domhandler@^2.3.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259"
|
||||
dependencies:
|
||||
domelementtype "1"
|
||||
|
||||
domutils@1.5, domutils@1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
|
||||
@@ -761,6 +799,13 @@ domutils@1.5, domutils@1.5.1:
|
||||
dom-serializer "0"
|
||||
domelementtype "1"
|
||||
|
||||
domutils@^1.5.1:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
|
||||
dependencies:
|
||||
dom-serializer "0"
|
||||
domelementtype "1"
|
||||
|
||||
dot-prop@^4.1.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
|
||||
@@ -793,7 +838,7 @@ entities@1.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26"
|
||||
|
||||
entities@~1.1.1:
|
||||
entities@^1.1.1, entities@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
|
||||
|
||||
@@ -1186,6 +1231,14 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.4:
|
||||
version "4.1.11"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
|
||||
|
||||
graphql-docs@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-docs/-/graphql-docs-0.2.0.tgz#cf803f9c9d354fa03e89073d74e419261a5bfa74"
|
||||
dependencies:
|
||||
marked "^0.3.5"
|
||||
request "^2.74.0"
|
||||
yargs "^5.0.0"
|
||||
|
||||
har-schema@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
|
||||
@@ -1431,6 +1484,17 @@ html-entities@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
|
||||
|
||||
htmlparser2@^3.9.1:
|
||||
version "3.9.2"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
|
||||
dependencies:
|
||||
domelementtype "^1.3.0"
|
||||
domhandler "^2.3.0"
|
||||
domutils "^1.5.1"
|
||||
entities "^1.1.1"
|
||||
inherits "^2.0.1"
|
||||
readable-stream "^2.0.2"
|
||||
|
||||
htmlparser2@~3.8.1:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068"
|
||||
@@ -1879,7 +1943,7 @@ load-json-file@^1.0.0:
|
||||
pinkie-promise "^2.0.0"
|
||||
strip-bom "^2.0.0"
|
||||
|
||||
lodash.assign@^4.2.0:
|
||||
lodash.assign@^4.1.0, lodash.assign@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
|
||||
|
||||
@@ -1907,7 +1971,7 @@ lodash.startswith@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c"
|
||||
|
||||
lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.2.1, lodash@~4.17.4:
|
||||
lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.2.1, lodash@~4.17.4:
|
||||
version "4.17.5"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
|
||||
|
||||
@@ -1967,6 +2031,10 @@ markdown@~0.5.0:
|
||||
dependencies:
|
||||
nopt "~2.1.1"
|
||||
|
||||
marked@^0.3.5:
|
||||
version "0.3.16"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.16.tgz#2f188b7dfcfa6540fe9940adaf0f3b791c9a5cba"
|
||||
|
||||
marked@^0.3.9:
|
||||
version "0.3.12"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519"
|
||||
@@ -2425,6 +2493,12 @@ parse5@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
|
||||
|
||||
parse5@^3.0.1:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
parseurl@~1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
|
||||
@@ -2623,6 +2697,10 @@ redent@^1.0.0:
|
||||
indent-string "^2.1.0"
|
||||
strip-indent "^1.0.1"
|
||||
|
||||
regenerator-runtime@^0.11.0:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
|
||||
regex-cache@^0.4.2:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
|
||||
@@ -2666,7 +2744,7 @@ repeating@^2.0.0:
|
||||
dependencies:
|
||||
is-finite "^1.0.0"
|
||||
|
||||
request@2, request@^2.55.0:
|
||||
request@2, request@^2.55.0, request@^2.74.0:
|
||||
version "2.83.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
|
||||
dependencies:
|
||||
@@ -3424,6 +3502,10 @@ window-size@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
|
||||
|
||||
window-size@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
|
||||
|
||||
wordwrap@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
|
||||
@@ -3475,6 +3557,13 @@ yallist@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||
|
||||
yargs-parser@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-3.2.0.tgz#5081355d19d9d0c8c5d81ada908cb4e6d186664f"
|
||||
dependencies:
|
||||
camelcase "^3.0.0"
|
||||
lodash.assign "^4.1.0"
|
||||
|
||||
yargs-parser@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
|
||||
@@ -3493,6 +3582,25 @@ yargs@^3.32.0:
|
||||
window-size "^0.1.4"
|
||||
y18n "^3.2.0"
|
||||
|
||||
yargs@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-5.0.0.tgz#3355144977d05757dbb86d6e38ec056123b3a66e"
|
||||
dependencies:
|
||||
cliui "^3.2.0"
|
||||
decamelize "^1.1.1"
|
||||
get-caller-file "^1.0.1"
|
||||
lodash.assign "^4.2.0"
|
||||
os-locale "^1.4.0"
|
||||
read-pkg-up "^1.0.1"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^1.0.1"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^1.0.2"
|
||||
which-module "^1.0.0"
|
||||
window-size "^0.2.0"
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^3.2.0"
|
||||
|
||||
yargs@^7.0.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
router.get('/docs', (req, res) => {
|
||||
res.render('admin/docs');
|
||||
});
|
||||
}
|
||||
|
||||
router.get('/confirm-email', (req, res) => {
|
||||
res.render('admin/confirm-email');
|
||||
});
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Talk: GraphQL Docs</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
|
||||
<style media="screen">
|
||||
body, #root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
background-color: #FAFAFA;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin: 0 auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
a {
|
||||
border-bottom: 2px dotted #f67150;
|
||||
color: #333 !important;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<%- include partials/head %>
|
||||
</head>
|
||||
<body class="docs-page">
|
||||
<div id="root"></div>
|
||||
<script src="<%= STATIC_URL %>static/coral-docs/bundle.js" charset="utf-8"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -22,7 +22,6 @@ debug(`Using ${pluginsPath} as the plugin configuration path`);
|
||||
const buildTargets = [
|
||||
'coral-admin',
|
||||
'coral-login',
|
||||
'coral-docs',
|
||||
{ name: 'coral-auth-callback', disablePolyfill: true },
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user