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

[v2] Added with-apollo-typescript example #891

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
13 changes: 13 additions & 0 deletions examples/with-apollo-typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
logs
*.log
npm-debug.log*
.DS_Store

coverage
node_modules
build
public/static
.env.local
.env.development.local
.env.test.local
.env.production.local
69 changes: 69 additions & 0 deletions examples/with-apollo-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Razzle ApolloClient with TypeScript Example

## How to use
Download the example [or clone the whole project](https://github.com/jaredpalmer/razzle.git):

```bash
curl https://codeload.github.com/jaredpalmer/razzle/tar.gz/master | tar -xz --strip=2 razzle-master/examples/with-apollo-typescript
cd with-apollo-typescript
```

Install it and run:

```bash
yarn install
yarn start
```

## Idea behind the example
Using techniques specified by [Apollo](https://www.apollographql.com/docs/react/features/server-side-rendering.html) and also following the [GitHunt](https://github.com/apollographql/GitHunt-React/blob/master/src/server.js) app as an example

This is an of how to use Razzle with [TypeScript](https://github.com/Microsoft/TypeScript).
In `razzle.config.js`, we locate the part of the webpack configuration
that is running `babel-loader` and swap it out for `ts-loader`.
Additionally, we make sure Razzle knows how to resolve `.ts` and `.tsx` files.

Lastly, we also need to modify our Jest configuration to handle typescript files.
Thus we add `ts-jest` and `@types/jest` to our dev dependencies. Then we augment Razzle's default jest setup by adding a field in our `package.json`.

```json
// package.json

{
...
"jest": {
"transform": {
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js",
"\\.css$": "<rootDir>/node_modules/razzle/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/node_modules/razzle/config/jest/fileTransform.js"
},
"testMatch": [
"<rootDir>/src/**/__tests__/**/*.(ts|js)?(x)",
"<rootDir>/src/**/?(*.)(spec|test).(ts|js)?(x)"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"json"
],
"collectCoverageFrom": [
"src/**/*.{js,jsx,ts,tsx}"
]
}
}
```

The `tslint.json` and `tsconfig.json` are taken from Microsoft's official
[TypeScript-React-Starter](https://github.com/Microsoft/TypeScript-React-Starter).

Note: You do not techincally _need_ to fully replace `babel-loader` with
`ts-loader` to use TypeScript. Both TS and Babel transpile ES6 code,
so when you run both webpack loaders you are making Razzle do twice the work. From our testing,
this can make HMR extremely slow on larger apps. Thus, this example overwrites
`babel-loader` with `ts-loader`. However, if you are incrementally moving to typescript you may want to run both loaders side by side. If you are running both, add this to your `jest.transform` setup in `package.json`:

```
"^.+\\.(js|jsx)$": "<rootDir>/node_modules/razzle/config/jest/babelTransform.js",
```
This will continue to transform .js files through babel.
62 changes: 62 additions & 0 deletions examples/with-apollo-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"name": "razzle-examples-with-typescript",
"version": "2.4.1",
"license": "MIT",
"scripts": {
"start": "razzle start",
"build": "razzle build",
"test": "razzle test --env=jsdom",
"start:prod": "NODE_ENV=production node build/server.js"
},
"dependencies": {
"apollo-cache-inmemory": "^1.4.2",
"apollo-client": "^2.4.12",
"apollo-link": "^1.2.6",
"apollo-link-error": "^1.1.5",
"apollo-link-http": "^1.5.9",
"express": "^4.16.4",
"graphql": "^14.1.1",
"node-fetch": "^2.3.0",
"react": "^16.7.0",
"react-apollo": "^2.3.3",
"react-dom": "^16.7.0",
"react-router-dom": "^4.3.1"
},
"devDependencies": {
"@types/express": "^4.16.0",
"@types/graphql": "^14.0.5",
"@types/jest": "^23.3.13",
"@types/node": "10.12.18",
"@types/node-fetch": "^2.1.4",
"@types/react": "^16.7.20",
"@types/react-dom": "^16.0.11",
"@types/react-router-dom": "^4.3.1",
"@types/webpack-env": "^1.13.6",
"razzle": "^2.4.1",
"razzle-plugin-typescript": "^2.4.1",
"ts-jest": "^23.10.5",
"tslint": "^5.12.1",
"tslint-react": "^3.6.0",
"typescript": "^3.2.4"
},
"jest": {
"transform": {
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js",
"\\.css$": "<rootDir>/node_modules/razzle/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/node_modules/razzle/config/jest/fileTransform.js"
},
"testMatch": [
"<rootDir>/src/**/__tests__/**/*.(ts|js)?(x)",
"<rootDir>/src/**/?(*.)(spec|test).(ts|js)?(x)"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"json"
],
"collectCoverageFrom": [
"src/**/*.{js,jsx,ts,tsx}"
]
}
}
Binary file not shown.
2 changes: 2 additions & 0 deletions examples/with-apollo-typescript/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
User-agent: *

5 changes: 5 additions & 0 deletions examples/with-apollo-typescript/razzle.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

module.exports = {
plugins: ['typescript'],
};
5 changes: 5 additions & 0 deletions examples/with-apollo-typescript/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
18 changes: 18 additions & 0 deletions examples/with-apollo-typescript/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { render } from 'react-dom';

import App from './App';

import { MemoryRouter } from 'react-router-dom';

describe('<App />', () => {
test('renders without exploding', () => {
const div = document.createElement('div');
render(
<MemoryRouter>
<App />
</MemoryRouter>,
div
);
});
});
13 changes: 13 additions & 0 deletions examples/with-apollo-typescript/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Home from './Home';

import './App.css';

const App = () => (
<Switch>
<Route exact={true} path="/" component={Home} />
</Switch>
);

export default App;
33 changes: 33 additions & 0 deletions examples/with-apollo-typescript/src/Home.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.Home {
text-align: center;
}

.Home-logo {
animation: Home-logo-spin infinite 20s linear;
height: 80px;
}

.Home-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}

.Home-intro {
font-size: large;
}

.Home-resources {
list-style: none;
}

.Home-resources > li {
display: inline-block;
padding: 1rem;
}

@keyframes Home-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
34 changes: 34 additions & 0 deletions examples/with-apollo-typescript/src/Home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import logo from './react.svg';

import './Home.css';

class Home extends React.Component<{}, {}> {
public render() {
return (
<div className="Home">
<div className="Home-header">
<img src={logo} className="Home-logo" alt="logo" />
<h2>Welcome to Razzles</h2>
</div>
<p className="Home-intro">
To get started, edit <code>src/App.tsx</code> or{' '}
<code>src/Home.tsx</code> and save to reload.
</p>
<ul className="Home-resources">
<li>
<a href="https://github.com/jaredpalmer/razzle">Docs</a>
</li>
<li>
<a href="https://github.com/jaredpalmer/razzle/issues">Issues</a>
</li>
<li>
<a href="https://palmer.chat">Community Slack</a>
</li>
</ul>
</div>
);
}
}

export default Home;
45 changes: 45 additions & 0 deletions examples/with-apollo-typescript/src/Html.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
function Html({
content,
state,
assets
}: {
content: string;
state: any;
assets: any;
}) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Razzle TypeScript</title>

{assets.client.css ? (
<link rel="stylesheet" href={assets.client.css} />
) : (
''
)}
{process.env.NODE_ENV === 'production' ? (
<script src={assets.client.js} defer={true} />
) : (
<script src={assets.client.js} defer={true} crossOrigin="anonymous" />
)}
</head>
<body>
<div id="root" dangerouslySetInnerHTML={{ __html: content }} />
<script
dangerouslySetInnerHTML={{
__html: `window.__APOLLO_STATE__=${JSON.stringify(state).replace(
/</g,
'\\u003c'
)};`
}}
/>
</body>
</html>
);
}

export default Html;
30 changes: 30 additions & 0 deletions examples/with-apollo-typescript/src/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { hydrate } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { ApolloProvider } from 'react-apollo';
import App from './App';

const client = new ApolloClient({
cache: new InMemoryCache().restore((window as any).__APOLLO_STATE__),
connectToDevTools: true,
link: new HttpLink({
credentials: 'same-origin'
}),
ssrForceFetchDelay: 100
});
hydrate(
<ApolloProvider client={client}>
<BrowserRouter>
<App />
</BrowserRouter>
</ApolloProvider>,
document.getElementById('root')
);

if (module.hot) {
module.hot.accept();
}
29 changes: 29 additions & 0 deletions examples/with-apollo-typescript/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import express from 'express';

// this require is necessary for server HMR to recover from error
// tslint:disable-next-line:no-var-requires
let app = require('./server').default;

if (module.hot) {
module.hot.accept('./server', () => {
console.log('🔁 HMR Reloading `./server`...');
try {
app = require('./server').default;
} catch (error) {
console.error(error);
}
});
console.info('✅ Server-side HMR Enabled!');
}

const port = process.env.PORT || 3000;

export default express()
.use((req, res) => app.handle(req, res))
.listen(port, (err: Error) => {
if (err) {
console.error(err);
return;
}
console.log(`> Started on port ${port}`);
});
6 changes: 6 additions & 0 deletions examples/with-apollo-typescript/src/react.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading