Skip to content

Instantly share code, notes, and snippets.

@tonyxiao
Created March 10, 2016 23:00
Show Gist options
  • Save tonyxiao/92e3d994eec0d762a17e to your computer and use it in GitHub Desktop.
Save tonyxiao/92e3d994eec0d762a17e to your computer and use it in GitHub Desktop.
Inject custom dochead into html returned by meteor server. Credit goes to https://github.com/thereactivestack/meteor-react-router-ssr
import React from 'react'
import {IndexRoute, Route} from 'react-router'
import Helmet from 'react-helmet'
const wrap = (Component) => (location, cb) => {
cb(null, Component)
}
const makeHelmet = (info) => {
const {title, description, image, url} = {
title: 'Your site title',
description: 'Your site description',
image: 'https://drscdn.500px.org/photo/100057305/q%3D80_m%3D2000/ac293a79b0845e8f4df02eba256077ce',
url: 'https://google.com',
...info
}
return <Helmet
title={title}
meta={[
{property: 'og:url', content: url},
{property: 'og:title', content: title},
{property: 'og:type', content: 'website'},
{property: 'og:description', content: description},
{property: 'og:image', content: image},
{name: 'description', content: description}
]} />
}
export default function ({Collections}) {
return (
<Route path='/'>
<IndexRoute component={() => makeHelmet()} />
<Route path='tonyx' component={() => makeHelmet({title: 'Tony Xiao'})} />
<Route path='topics/:topicId' getComponent={wrap(({params}) => {
const topicId = Collections.Topics.findOne(params.topicId)
return topicId ? makeHelmet({
title: topicId.displayName,
url: topicId.cover && channel.cover.url,
description: topicId.description
}) : <noscript />
})} />
<Route path='*' component={() => makeHelmet()} />
</Route>
)
}
import {Meteor} from 'meteor/meteor'
import {WebApp} from 'meteor/webapp'
import {RoutePolicy} from 'meteor/routepolicy'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import {match, RouterContext} from 'react-router'
import Helmet from 'react-helmet'
// meteor algorithm to check if this is a meteor serving http request or not
const IsAppUrl = (url) => {
if (url === '/favicon.ico' || url === '/robots.txt') {
return false
}
// NOTE: app.manifest is not a web standard like favicon.ico and
// robots.txt. It is a file name we have chosen to use for HTML5
// appcache URLs. It is included here to prevent using an appcache
// then removing it from poisoning an app permanently. Eventually,
// once we have server side routing, this won't be needed as
// unknown URLs with return a 404 automatically.
if (url === '/app.manifest') {
return false
}
// Avoid serving app HTML for declared routes such as /sockjs/.
if (RoutePolicy.classify(url)) {
return false
}
return true
}
const patchResWrite = (originalWrite, head) => {
return function (data) {
if (typeof data === 'string' && data.indexOf('<!DOCTYPE html>') === 0) {
// Add react-helmet stuff in the header (yay SEO!)
data = data.replace('<head>',
`<head>${head.title}${head.base}${head.meta}${head.link}${head.script}`)
// '<head>' + head.title + head.base + head.meta + head.link + head.script
}
originalWrite.call(this, data)
}
}
export default Meteor.bindEnvironment((routes) => {
console.log('Will inject dochead handler into Meteor')
WebApp.connectHandlers.use(Meteor.bindEnvironment((req, res, next) => {
if (!IsAppUrl(req.url)) {
return next()
}
match({routes, location: req.url}, Meteor.bindEnvironment((err, redirectLocation, renderProps) => {
if (!err && !redirectLocation && renderProps) {
console.log('Will patch res.write with dochead info url =', req.url)
ReactDOMServer.renderToStaticMarkup(React.createElement(RouterContext, renderProps))
const head = Helmet.rewind()
res.write = patchResWrite(res.write, head)
}
next()
}))
}))
})
import context from './configs/context'
import initDocheadRoutes from './dochead/docheadRoutes.jsx'
import injectDochead from './dochead/injectDochead'
injectDochead(initDocheadRoutes(context))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment