1
0
Fork 0
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:
Sampson Oliver 2018-05-21 18:25:45 +10:00 committed by Tim Neutkens
parent f2261050a0
commit 35d32b48cc
20 changed files with 366 additions and 2 deletions

View file

@ -0,0 +1,5 @@
{
"projects": {
"default": "<project-name-here>"
}
}

View file

@ -0,0 +1 @@
dist/

View 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`.

View 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"
}
}

View 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"
}
}

View file

@ -0,0 +1,3 @@
{
"presets": ["next/babel", "@zeit/next-typescript/babel"]
}

View file

@ -0,0 +1,10 @@
import Header from './Header';
const App = ({ children }: { children?: any }) => (
<main>
<Header />
{children}
</main>
);
export default App;

View file

@ -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>
);

View file

@ -0,0 +1,2 @@
const withTypescript = require('@zeit/next-typescript')
module.exports = withTypescript({ distDir: '../../dist/functions/next' })

View file

@ -0,0 +1,7 @@
import App from '../components/App';
export default () => (
<App>
<p>About Page</p>
</App>
);

View file

@ -0,0 +1,7 @@
import App from '../components/App';
export default () => (
<App>
<p>Index Page</p>
</App>
);

View file

@ -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": ".",
}
}

View file

@ -0,0 +1,7 @@
{
"extends": ["tslint:latest", "tslint-react"],
"rules": {
"quotemark": [true, "single"],
"no-submodule-imports": false
}
}

View file

@ -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));
});

View file

@ -0,0 +1,3 @@
import * as functions from 'firebase-functions';
export { nextApp } from './app/next';

View file

@ -0,0 +1,13 @@
{
"compilerOptions": {
"lib": ["es6"],
"module": "commonjs",
"noImplicitReturns": true,
"outDir": "../../dist/functions",
"sourceMap": true,
"target": "es6",
"baseUrl": "./src"
},
"compileOnSave": true,
"include": ["src"]
}

View file

@ -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"
}

View file

@ -1,3 +1,3 @@
export const Hello = () => ( export const Hello = () => (
<div>Hello</div> <div>Hello</div>
) )

View file

@ -1,3 +1,3 @@
export const World = () => ( export const World = () => (
<div>World</div> <div>World</div>
) )