mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Create with-firebase-hosting-and-typescript example (#4443)
Adds an example based off of @jthegedus work on firebase hosting, compatible with next v6 and using typescript in both the firebase functions and the next app.
This commit is contained in:
parent
f2261050a0
commit
35d32b48cc
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
"default": "<project-name-here>"
|
||||||
|
}
|
||||||
|
}
|
1
examples/with-firebase-hosting-and-typescript/.gitignore
vendored
Normal file
1
examples/with-firebase-hosting-and-typescript/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
dist/
|
74
examples/with-firebase-hosting-and-typescript/README.md
Normal file
74
examples/with-firebase-hosting-and-typescript/README.md
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# With Firebase Hosting and Typescript example
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
### Using `create-next-app`
|
||||||
|
|
||||||
|
Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-next-app --example with-firebase-hosting-and-typescript with-firebase-hosting-and-typescript-app
|
||||||
|
# or
|
||||||
|
yarn create next-app --example with-firebase-hosting-and-typescript with-firebase-hosting-and-typescript-app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download manually
|
||||||
|
|
||||||
|
Download the example [or clone the repo](https://github.com/zeit/next.js):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-firebase-hosting-and-typescript
|
||||||
|
cd with-firebase-hosting-and-typescript
|
||||||
|
```
|
||||||
|
|
||||||
|
Set up firebase:
|
||||||
|
|
||||||
|
* install Firebase Tools: `npm i -g firebase-tools`
|
||||||
|
* create a project through the [firebase web console](https://console.firebase.google.com/)
|
||||||
|
* grab the projects ID from the web consoles URL: https://console.firebase.google.com/project/<projectId>
|
||||||
|
* update the `.firebaserc` default project ID to the newly created project
|
||||||
|
* login to the Firebase CLI tool with `firebase login`
|
||||||
|
|
||||||
|
#### Install project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Run Next.js development:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Run Firebase locally for testing:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Deploy it to the cloud with Firebase:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Clean dist folder
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## The idea behind the example
|
||||||
|
|
||||||
|
The goal is to host the Next.js app on Firebase Cloud Functions with Firebase Hosting rewrite rules so our app is served from our Firebase Hosting URL, with a complete Typescript stack for both the Next app and for the Firebase Functions. Each individual `page` bundle is served in a new call to the Cloud Function which performs the initial server render.
|
||||||
|
|
||||||
|
This is based off of the work of @jthegedus in the [with-firebase-hosting](https://github.com/zeit/next.js/tree/canary/examples/with-firebase-hosting) example.
|
||||||
|
|
||||||
|
If you're having issues, feel free to tag @sampsonjoliver in the [issue you create on the next.js repo](https://github.com/zeit/next.js/issues/new)
|
||||||
|
|
||||||
|
## Important
|
||||||
|
|
||||||
|
* The empty `placeholder.html` file is so Firebase Hosting does not error on an empty `public/` folder and still hosts at the Firebase project URL.
|
||||||
|
* `firebase.json` outlines the catchall rewrite rule for our Cloud Function.
|
||||||
|
* The [Firebase predeploy](https://firebase.google.com/docs/cli/#predeploy_and_postdeploy_hooks) hooks defined in `firebase.json` will handle linting and compiling of the next app and the functions sourceswhen `firebase deploy` is invoked. The only scripts you should need are `dev`, `clean` and `deploy`.
|
24
examples/with-firebase-hosting-and-typescript/firebase.json
Normal file
24
examples/with-firebase-hosting-and-typescript/firebase.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"functions": {
|
||||||
|
"source": "dist/functions",
|
||||||
|
"predeploy": [
|
||||||
|
"npm run lint-functions",
|
||||||
|
"npm run lint-app",
|
||||||
|
"npm run typecheck-app",
|
||||||
|
"npm run build-functions",
|
||||||
|
"npm run build-app",
|
||||||
|
"npm run copy-deps",
|
||||||
|
"npm run install-deps"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hosting": {
|
||||||
|
"public": "dist/public",
|
||||||
|
"rewrites": [
|
||||||
|
{
|
||||||
|
"source": "**/**",
|
||||||
|
"function": "nextApp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"predeploy": "npm run build-public"
|
||||||
|
}
|
||||||
|
}
|
38
examples/with-firebase-hosting-and-typescript/package.json
Normal file
38
examples/with-firebase-hosting-and-typescript/package.json
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"name": "with-firebase-hosting",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Host Next.js SSR app on Firebase Cloud Functions with Firebase Hosting redirects.",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next src/app",
|
||||||
|
"serve": "NODE_ENV=production firebase serve --only functions,hosting",
|
||||||
|
"deploy": "firebase deploy",
|
||||||
|
"clean": "rimraf \"dist/functions\" && rimraf \"dist/public\"",
|
||||||
|
"build-app": "next build \"src/app\"",
|
||||||
|
"build-public": "cpx \"src/public/**/*.*\" \"dist/public\" -C",
|
||||||
|
"build-functions": "tsc --project src/functions",
|
||||||
|
"lint-app": "tslint --project src/app",
|
||||||
|
"typecheck-app": "tsc --project src/app",
|
||||||
|
"lint-functions": "tslint --project src/functions",
|
||||||
|
"copy-deps": "cpx \"*{package.json,package-lock.json,yarn.lock}\" \"dist/functions\"",
|
||||||
|
"install-deps": "cd \"dist/functions\" && npm i"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@zeit/next-typescript": "^1.0.0",
|
||||||
|
"firebase-admin": "~5.12.1",
|
||||||
|
"firebase-functions": "^1.0.1",
|
||||||
|
"next": "^6.0.3",
|
||||||
|
"react": "^16.3.2",
|
||||||
|
"react-dom": "^16.3.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/next": "^2.4.10",
|
||||||
|
"@types/react": "^16.3.14",
|
||||||
|
"cpx": "1.5.0",
|
||||||
|
"firebase-tools": "3.18.4",
|
||||||
|
"prettier": "1.12.1",
|
||||||
|
"rimraf": "2.6.2",
|
||||||
|
"tslint": "^5.8.0",
|
||||||
|
"tslint-react": "3.6.0",
|
||||||
|
"typescript": "^2.5.3"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"presets": ["next/babel", "@zeit/next-typescript/babel"]
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import Header from './Header';
|
||||||
|
|
||||||
|
const App = ({ children }: { children?: any }) => (
|
||||||
|
<main>
|
||||||
|
<Header />
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export default ({ pathname }: { pathname?: any }) => (
|
||||||
|
<header>
|
||||||
|
<Link href='/'>
|
||||||
|
<a className={pathname === '/' ? 'is-active' : ''}>Home</a>
|
||||||
|
</Link>
|
||||||
|
<Link href='/about'>
|
||||||
|
<a className={pathname === '/about' ? 'is-active' : ''}>About</a>
|
||||||
|
</Link>
|
||||||
|
</header>
|
||||||
|
);
|
|
@ -0,0 +1,2 @@
|
||||||
|
const withTypescript = require('@zeit/next-typescript')
|
||||||
|
module.exports = withTypescript({ distDir: '../../dist/functions/next' })
|
|
@ -0,0 +1,7 @@
|
||||||
|
import App from '../components/App';
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<App>
|
||||||
|
<p>About Page</p>
|
||||||
|
</App>
|
||||||
|
);
|
|
@ -0,0 +1,7 @@
|
||||||
|
import App from '../components/App';
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<App>
|
||||||
|
<p>Index Page</p>
|
||||||
|
</App>
|
||||||
|
);
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
"allowJs": false,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"strict": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"preserveConstEnums": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"lib": [
|
||||||
|
"es6",
|
||||||
|
"dom",
|
||||||
|
"es2016"
|
||||||
|
],
|
||||||
|
"baseUrl": ".",
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": ["tslint:latest", "tslint-react"],
|
||||||
|
"rules": {
|
||||||
|
"quotemark": [true, "single"],
|
||||||
|
"no-submodule-imports": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import * as functions from 'firebase-functions';
|
||||||
|
const next = require('next');
|
||||||
|
|
||||||
|
const dev = process.env.NODE_ENV !== 'production';
|
||||||
|
const app = next({ dev, conf: { distDir: 'next' } });
|
||||||
|
const handle = app.getRequestHandler();
|
||||||
|
|
||||||
|
export const nextApp = functions.https.onRequest((req, res) => {
|
||||||
|
console.log('File: ' + req.originalUrl);
|
||||||
|
return app.prepare().then(() => handle(req, res));
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
import * as functions from 'firebase-functions';
|
||||||
|
|
||||||
|
export { nextApp } from './app/next';
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["es6"],
|
||||||
|
"module": "commonjs",
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"outDir": "../../dist/functions",
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "es6",
|
||||||
|
"baseUrl": "./src"
|
||||||
|
},
|
||||||
|
"compileOnSave": true,
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
// -- Strict errors --
|
||||||
|
// These lint rules are likely always a good idea.
|
||||||
|
|
||||||
|
// Force function overloads to be declared together. This ensures readers understand APIs.
|
||||||
|
"adjacent-overload-signatures": true,
|
||||||
|
|
||||||
|
// Do not allow the subtle/obscure comma operator.
|
||||||
|
"ban-comma-operator": true,
|
||||||
|
|
||||||
|
// Do not allow internal modules or namespaces . These are deprecated in favor of ES6 modules.
|
||||||
|
"no-namespace": true,
|
||||||
|
|
||||||
|
// Do not allow parameters to be reassigned. To avoid bugs, developers should instead assign new values to new vars.
|
||||||
|
"no-parameter-reassignment": true,
|
||||||
|
|
||||||
|
// Force the use of ES6-style imports instead of /// <reference path=> imports.
|
||||||
|
"no-reference": true,
|
||||||
|
|
||||||
|
// Do not allow type assertions that do nothing. This is a big warning that the developer may not understand the
|
||||||
|
// code currently being edited (they may be incorrectly handling a different type case that does not exist).
|
||||||
|
"no-unnecessary-type-assertion": true,
|
||||||
|
|
||||||
|
// Disallow nonsensical label usage.
|
||||||
|
"label-position": true,
|
||||||
|
|
||||||
|
// Disallows the (often typo) syntax if (var1 = var2). Replace with if (var2) { var1 = var2 }.
|
||||||
|
"no-conditional-assignment": true,
|
||||||
|
|
||||||
|
// Disallows constructors for primitive types (e.g. new Number('123'), though Number('123') is still allowed).
|
||||||
|
"no-construct": true,
|
||||||
|
|
||||||
|
// Do not allow super() to be called twice in a constructor.
|
||||||
|
"no-duplicate-super": true,
|
||||||
|
|
||||||
|
// Do not allow the same case to appear more than once in a switch block.
|
||||||
|
"no-duplicate-switch-case": true,
|
||||||
|
|
||||||
|
// Do not allow a variable to be declared more than once in the same block. Consider function parameters in this
|
||||||
|
// rule.
|
||||||
|
"no-duplicate-variable": [true, "check-parameters"],
|
||||||
|
|
||||||
|
// Disallows a variable definition in an inner scope from shadowing a variable in an outer scope. Developers should
|
||||||
|
// instead use a separate variable name.
|
||||||
|
"no-shadowed-variable": true,
|
||||||
|
|
||||||
|
// Empty blocks are almost never needed. Allow the one general exception: empty catch blocks.
|
||||||
|
"no-empty": [true, "allow-empty-catch"],
|
||||||
|
|
||||||
|
// Functions must either be handled directly (e.g. with a catch() handler) or returned to another function.
|
||||||
|
// This is a major source of errors in Cloud Functions and the team strongly recommends leaving this rule on.
|
||||||
|
"no-floating-promises": true,
|
||||||
|
|
||||||
|
// Do not allow any imports for modules that are not in package.json. These will almost certainly fail when
|
||||||
|
// deployed.
|
||||||
|
"no-implicit-dependencies": true,
|
||||||
|
|
||||||
|
// The 'this' keyword can only be used inside of classes.
|
||||||
|
"no-invalid-this": true,
|
||||||
|
|
||||||
|
// Do not allow strings to be thrown because they will not include stack traces. Throw Errors instead.
|
||||||
|
"no-string-throw": true,
|
||||||
|
|
||||||
|
// Disallow control flow statements, such as return, continue, break, and throw in finally blocks.
|
||||||
|
"no-unsafe-finally": true,
|
||||||
|
|
||||||
|
// Do not allow variables to be used before they are declared.
|
||||||
|
"no-use-before-declare": true,
|
||||||
|
|
||||||
|
// Expressions must always return a value. Avoids common errors like const myValue = functionReturningVoid();
|
||||||
|
"no-void-expression": [true, "ignore-arrow-function-shorthand"],
|
||||||
|
|
||||||
|
// Disallow duplicate imports in the same file.
|
||||||
|
"no-duplicate-imports": true,
|
||||||
|
|
||||||
|
|
||||||
|
// -- Strong Warnings --
|
||||||
|
// These rules should almost never be needed, but may be included due to legacy code.
|
||||||
|
// They are left as a warning to avoid frustration with blocked deploys when the developer
|
||||||
|
// understand the warning and wants to deploy anyway.
|
||||||
|
|
||||||
|
// Warn when an empty interface is defined. These are generally not useful.
|
||||||
|
"no-empty-interface": {"severity": "warning"},
|
||||||
|
|
||||||
|
// Warn when an import will have side effects.
|
||||||
|
"no-import-side-effect": {"severity": "warning"},
|
||||||
|
|
||||||
|
// Warn when variables are defined with var. Var has subtle meaning that can lead to bugs. Strongly prefer const for
|
||||||
|
// most values and let for values that will change.
|
||||||
|
"no-var-keyword": {"severity": "warning"},
|
||||||
|
|
||||||
|
// Prefer === and !== over == and !=. The latter operators support overloads that are often accidental.
|
||||||
|
"triple-equals": {"severity": "warning"},
|
||||||
|
|
||||||
|
// Warn when using deprecated APIs.
|
||||||
|
"deprecation": {"severity": "warning"},
|
||||||
|
|
||||||
|
// -- Light Warnigns --
|
||||||
|
// These rules are intended to help developers use better style. Simpler code has fewer bugs. These would be "info"
|
||||||
|
// if TSLint supported such a level.
|
||||||
|
|
||||||
|
// prefer for( ... of ... ) to an index loop when the index is only used to fetch an object from an array.
|
||||||
|
// (Even better: check out utils like .map if transforming an array!)
|
||||||
|
"prefer-for-of": {"severity": "warning"},
|
||||||
|
|
||||||
|
// Warns if function overloads could be unified into a single function with optional or rest parameters.
|
||||||
|
"unified-signatures": {"severity": "warning"},
|
||||||
|
|
||||||
|
// Warns if code has an import or variable that is unused.
|
||||||
|
"no-unused-variable": {"severity": "warning"},
|
||||||
|
|
||||||
|
// Prefer const for values that will not change. This better documents code.
|
||||||
|
"prefer-const": {"severity": "warning"},
|
||||||
|
|
||||||
|
// Multi-line object liiterals and function calls should have a trailing comma. This helps avoid merge conflicts.
|
||||||
|
"trailing-comma": {"severity": "warning"}
|
||||||
|
},
|
||||||
|
|
||||||
|
"defaultSeverity": "error"
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
export const Hello = () => (
|
export const Hello = () => (
|
||||||
<div>Hello</div>
|
<div>Hello</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export const World = () => (
|
export const World = () => (
|
||||||
<div>World</div>
|
<div>World</div>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue