From 905ff32b7f45810847fd70a7642bda3149870589 Mon Sep 17 00:00:00 2001 From: Sitian Liu Date: Tue, 6 Feb 2018 01:52:31 -0500 Subject: [PATCH 01/64] Create example using custom charset (#3697) https://github.com/zeit/next.js/issues/3401 --- examples/custom-charset/package.json | 14 ++++++++++ examples/custom-charset/pages/README.md | 34 +++++++++++++++++++++++++ examples/custom-charset/pages/index.js | 3 +++ examples/custom-charset/server.js | 21 +++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 examples/custom-charset/package.json create mode 100644 examples/custom-charset/pages/README.md create mode 100644 examples/custom-charset/pages/index.js create mode 100644 examples/custom-charset/server.js diff --git a/examples/custom-charset/package.json b/examples/custom-charset/package.json new file mode 100644 index 00000000..d9a8a33c --- /dev/null +++ b/examples/custom-charset/package.json @@ -0,0 +1,14 @@ +{ + "name": "custom-server", + "version": "1.0.0", + "scripts": { + "dev": "node server.js", + "build": "next build", + "start": "NODE_ENV=production node server.js" + }, + "dependencies": { + "next": "latest", + "react": "^16.0.0", + "react-dom": "^16.0.0" + } +} diff --git a/examples/custom-charset/pages/README.md b/examples/custom-charset/pages/README.md new file mode 100644 index 00000000..cc4798c6 --- /dev/null +++ b/examples/custom-charset/pages/README.md @@ -0,0 +1,34 @@ +[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/custom-server) + +# Custom server example + +## How to use + +### 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/custom-charset +cd custom-charset +``` + +Install it and run: + +```bash +npm install +npm run dev +``` + +Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)) + +```bash +now +``` + +## The idea behind the example + +The HTTP/1.1 specification says - if charset is not set in the http header then the browser defaults use ISO-8859-1. +For languages like Polish, Albanian, Hungarian, Czech, Slovak, Slovene, there will be broken characters encoding from SSR. + +You can overwrite Content-Type in getInitialProps. But if you want to handle it as a server side concern, you can use this as an simple example. diff --git a/examples/custom-charset/pages/index.js b/examples/custom-charset/pages/index.js new file mode 100644 index 00000000..0e0c34eb --- /dev/null +++ b/examples/custom-charset/pages/index.js @@ -0,0 +1,3 @@ +import React from 'react' + +export default () =>
áéíóöúü
diff --git a/examples/custom-charset/server.js b/examples/custom-charset/server.js new file mode 100644 index 00000000..bf8b0a43 --- /dev/null +++ b/examples/custom-charset/server.js @@ -0,0 +1,21 @@ +const { createServer } = require('http') +const { parse } = require('url') +const next = require('next') + +const port = parseInt(process.env.PORT, 10) || 3000 +const dev = process.env.NODE_ENV !== 'production' +const app = next({ dev }) +const handle = app.getRequestHandler() + +app.prepare() +.then(() => { + createServer((req, res) => { + const parsedUrl = parse(req.url, true) + res.setHeader('Content-Type', 'text/html; charset=iso-8859-2') + handle(req, res, parsedUrl) + }) + .listen(port, (err) => { + if (err) throw err + console.log(`> Ready on http://localhost:${port}`) + }) +}) From efed85e495142b8f575dad431f0e8b7dcf7652f4 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Mon, 5 Feb 2018 23:18:38 -0800 Subject: [PATCH 02/64] Fix webpack-bundle-analyzer example to work with Next 5 (#3692) --- examples/with-webpack-bundle-analyzer/next.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/with-webpack-bundle-analyzer/next.config.js b/examples/with-webpack-bundle-analyzer/next.config.js index ad085831..1bdbe095 100644 --- a/examples/with-webpack-bundle-analyzer/next.config.js +++ b/examples/with-webpack-bundle-analyzer/next.config.js @@ -2,11 +2,11 @@ const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') const { ANALYZE } = process.env module.exports = { - webpack: function (config) { + webpack: function (config, { isServer }) { if (ANALYZE) { config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'server', - analyzerPort: 8888, + analyzerPort: isServer ? 8888 : 8889, openAnalyzer: true })) } From 8942d20b8c976ea9057bab2ba4974e3b522b9742 Mon Sep 17 00:00:00 2001 From: Bertrand Marron Date: Tue, 6 Feb 2018 10:08:53 +0100 Subject: [PATCH 03/64] Move react{-dom,} to the common chunk in production too (#3690) Without this, react-dom gets included in multiple pages. --- server/build/webpack.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/build/webpack.js b/server/build/webpack.js index 3084a37f..d2b61ccb 100644 --- a/server/build/webpack.js +++ b/server/build/webpack.js @@ -250,11 +250,11 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer // We need to move react-dom explicitly into common chunks. // Otherwise, if some other page or module uses it, it might // included in that bundle too. - if (dev && module.context && module.context.indexOf(`${sep}react${sep}`) >= 0) { + if (module.context && module.context.indexOf(`${sep}react${sep}`) >= 0) { return true } - if (dev && module.context && module.context.indexOf(`${sep}react-dom${sep}`) >= 0) { + if (module.context && module.context.indexOf(`${sep}react-dom${sep}`) >= 0) { return true } From f9b52cfcb6cba5f2af93caf3cc8bc3636571323f Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Tue, 6 Feb 2018 13:09:41 +0100 Subject: [PATCH 04/64] Include next/dist/pages instead of exclude (patch) (#3704) * Move security related test cases into a its own file. * Removes the unused renderScript function * Add a nerv example. (#3573) * Add a nerv example. * Fix for indentation/style * Fix for name * Release 5.0.0 * Add multi-zones docs. (#3688) * Include next/dist/pages * Fix linting --- package.json | 2 +- readme.md | 43 +++++++++++++++++++++++++++++++++++++++++ server/build/webpack.js | 10 +++++++++- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 63097d47..4636afcb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "5.0.0-universal-alpha.14", + "version": "5.0.0", "description": "Minimalistic framework for server-rendered React applications", "main": "./dist/server/next.js", "license": "MIT", diff --git a/readme.md b/readme.md index d74bfe19..60352fcf 100644 --- a/readme.md +++ b/readme.md @@ -44,6 +44,7 @@ Next.js is a minimalistic framework for server-rendered React applications. - [CDN support with Asset Prefix](#cdn-support-with-asset-prefix) - [Production deployment](#production-deployment) - [Static HTML export](#static-html-export) +- [Multi Zones](#multi-zones) - [Recipes](#recipes) - [FAQ](#faq) - [Contributing](#contributing) @@ -1220,6 +1221,48 @@ So, you could only use `pathname`, `query` and `asPath` fields of the `context` > Basically, you won't be able to render HTML content dynamically as we pre-build HTML files. If you need that, you need run your app with `next start`. +## Multi Zones + +

+ Examples + +

+ +A zone is a single deployment of a Next.js app. Just like that, you can have multiple zones. Then you can merge them as a single app. + +For an example, you can have two zones like this: + +* https://docs.my-app.com for serving `/docs/**` +* https://ui.my-app.com for serving all other pages + +With multi zones support, you can merge both these apps into a single one. Which allows your customers to browse it using a single URL. But you can develop and deploy both apps independently. + +> This is exactly the same concept as microservices, but for frontend apps. + +### How to define a zone + +There are no special zones related APIs. You only need to do following things: + +* Make sure to keep only the pages you need in your app. (For an example, https://ui.my-app.com should not contain pages for `/docs/**`) +* Make sure your app has an [assetPrefix](https://github.com/zeit/next.js#cdn-support-with-asset-prefix). (You can also define the assetPrefix [dynamically](https://github.com/zeit/next.js#dynamic-assetprefix).) + +### How to merge them + +You can merge zones using any HTTP proxy. + +You can use [micro proxy](https://github.com/zeit/micro-proxy) as your local proxy server. It allows you to easily define routing rules like below: + +```json +{ + "rules": [ + {"pathname": "/docs**", "method":["GET", "POST", "OPTIONS"], "dest": "https://docs.my-app.com"}, + {"pathname": "/**", "dest": "https://ui.my-app.com"} + ] +} +``` + +For the production deployment, you can use the [path alias](https://zeit.co/docs/features/path-aliases) feature if you are using [ZEIT now](https://zeit.co/now). Otherwise, you can configure your existing proxy server to route HTML pages using a set of rules as show above. + ## Recipes - [Setting up 301 redirects](https://www.raygesualdo.com/posts/301-redirects-with-nextjs/) diff --git a/server/build/webpack.js b/server/build/webpack.js index d2b61ccb..c9c22f9f 100644 --- a/server/build/webpack.js +++ b/server/build/webpack.js @@ -71,7 +71,15 @@ function externalsConfig (dir, isServer) { } // Webpack itself has to be compiled because it doesn't always use module relative paths - if (res.match(/node_modules[/\\].*\.js/) && !res.match(/node_modules[/\\]webpack/)) { + if (res.match(/node_modules[/\\]next[/\\]dist[/\\]pages/)) { + return callback() + } + + if (res.match(/node_modules[/\\]webpack/)) { + return callback() + } + + if (res.match(/node_modules[/\\].*\.js/)) { return callback(null, `commonjs ${request}`) } From 2473b558716c35d9827f2daa9d64dad8cbf14e11 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Tue, 6 Feb 2018 13:21:49 +0100 Subject: [PATCH 05/64] [ssr-caching] Only cache 200 status requests (#3708) * [ssr-caching] Only cache 200 requests I'm assuming caching an error page is a bad pattern. * Update server.js --- examples/ssr-caching/server.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/examples/ssr-caching/server.js b/examples/ssr-caching/server.js index 11a1d98e..8f0ef4d8 100644 --- a/examples/ssr-caching/server.js +++ b/examples/ssr-caching/server.js @@ -45,26 +45,32 @@ function getCacheKey (req) { return `${req.url}` } -function renderAndCache (req, res, pagePath, queryParams) { +async function renderAndCache (req, res, pagePath, queryParams) { const key = getCacheKey(req) // If we have a page in the cache, let's serve it if (ssrCache.has(key)) { - console.log(`CACHE HIT: ${key}`) + res.setHeader('x-cache', 'HIT') res.send(ssrCache.get(key)) return } - // If not let's render the page into HTML - app.renderToHTML(req, res, pagePath, queryParams) - .then((html) => { - // Let's cache this page - console.log(`CACHE MISS: ${key}`) - ssrCache.set(key, html) + try { + // If not let's render the page into HTML + const html = await app.renderToHTML(req, res, pagePath, queryParams) + // Something is wrong with the request, let's skip the cache + if (res.statusCode !== 200) { res.send(html) - }) - .catch((err) => { - app.renderError(err, req, res, pagePath, queryParams) - }) + return + } + + // Let's cache this page + ssrCache.set(key, html) + + res.setHeader('x-cache', 'MISS') + res.send(html) + } catch (err) { + app.renderError(err, req, res, pagePath, queryParams) + } } From 2a845a61b747b71adc4df84f9261391d12547733 Mon Sep 17 00:00:00 2001 From: Romain Quellec Date: Tue, 6 Feb 2018 15:22:32 +0100 Subject: [PATCH 06/64] Correct url to blog post (#3709) --- examples/with-reasonml/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/with-reasonml/README.md b/examples/with-reasonml/README.md index 65ff6a02..60953681 100644 --- a/examples/with-reasonml/README.md +++ b/examples/with-reasonml/README.md @@ -40,7 +40,7 @@ Run BuckleScript build system `bsb -w` and `next -w` separately. For the sake of simple convention, `npm run dev` run both `bsb` and `next` concurrently. However, this doesn't offer the full [colorful and very, very, veeeery nice error -output](https://reasonml.github.io/community/blog/#way-way-waaaay-nicer-error-messages) +output](https://reasonml.github.io/blog/2017/08/25/way-nicer-error-messages.html) experience that ReasonML can offer, don't miss it! ## The idea behind the example From 9404beb12f8e8d099f03484088c495f470c49256 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Tue, 6 Feb 2018 16:09:01 +0100 Subject: [PATCH 07/64] Use latest instead of zones for with-zones example (#3710) --- examples/with-zones/blog/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/with-zones/blog/package.json b/examples/with-zones/blog/package.json index fcd53769..72691fe9 100644 --- a/examples/with-zones/blog/package.json +++ b/examples/with-zones/blog/package.json @@ -6,7 +6,7 @@ "start": "next start -p 5000" }, "dependencies": { - "next": "zones", + "next": "latest", "react": "^16.0.0", "react-dom": "^16.0.0" }, From 87ab61eba51ebe5bd1e8b0cbc0f41b5d435da28c Mon Sep 17 00:00:00 2001 From: Bonggyun Lee Date: Wed, 7 Feb 2018 01:06:48 +0900 Subject: [PATCH 08/64] example/with-typescript (#3698) * example/with-typescript2 * apply standard js style * remove unnecessary file & code * fix link url * replace with-typescript --- examples/with-typescript/.gitignore | 1 - examples/with-typescript/README.md | 1 - examples/with-typescript/next.config.js | 2 ++ examples/with-typescript/package.json | 20 +++++++++----------- examples/with-typescript/tsconfig.json | 25 +++++++++++++++++++++---- examples/with-typescript/tslint.json | 10 ---------- 6 files changed, 32 insertions(+), 27 deletions(-) delete mode 100644 examples/with-typescript/.gitignore create mode 100644 examples/with-typescript/next.config.js delete mode 100644 examples/with-typescript/tslint.json diff --git a/examples/with-typescript/.gitignore b/examples/with-typescript/.gitignore deleted file mode 100644 index a6c7c285..00000000 --- a/examples/with-typescript/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js diff --git a/examples/with-typescript/README.md b/examples/with-typescript/README.md index 3ccfe2b1..bb1a408e 100644 --- a/examples/with-typescript/README.md +++ b/examples/with-typescript/README.md @@ -31,4 +31,3 @@ npm install npm run dev ``` -Output JS files are aside the related TypeScript ones. diff --git a/examples/with-typescript/next.config.js b/examples/with-typescript/next.config.js new file mode 100644 index 00000000..d8b638bd --- /dev/null +++ b/examples/with-typescript/next.config.js @@ -0,0 +1,2 @@ +const withTypescript = require('@zeit/next-typescript') +module.exports = withTypescript() diff --git a/examples/with-typescript/package.json b/examples/with-typescript/package.json index 5311bd17..cf30b7f6 100644 --- a/examples/with-typescript/package.json +++ b/examples/with-typescript/package.json @@ -1,22 +1,20 @@ { - "name": "with-typescript", + "name": "with-typescript-plugin", "version": "1.0.0", "scripts": { - "dev": "concurrently \"tsc --pretty --watch\" \"next\"", - "prebuild": "tsc", + "dev": "next", "build": "next build", "start": "next start" }, "dependencies": { - "next": "latest", - "react": "^16.1.0", - "react-dom": "^16.1.0" + "next": "^5.0.0", + "react": "^16.2.0", + "react-dom": "^16.2.0" }, "devDependencies": { - "@types/next": "^2.4.5", - "@types/react": "^16.0.22", - "concurrently": "^3.5.0", - "tslint": "^5.8.0", - "typescript": "^2.6.1" + "@types/next": "^2.4.7", + "@types/react": "^16.0.36", + "@zeit/next-typescript": "0.0.8", + "typescript": "^2.7.1" } } diff --git a/examples/with-typescript/tsconfig.json b/examples/with-typescript/tsconfig.json index 804cc32c..4ce1dc5d 100644 --- a/examples/with-typescript/tsconfig.json +++ b/examples/with-typescript/tsconfig.json @@ -1,8 +1,25 @@ { + "compileOnSave": false, "compilerOptions": { - "jsx": "react-native", - "module": "commonjs", - "strict": true, - "target": "es2017" + "target": "esnext", + "module": "esnext", + "allowJs": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "removeComments": false, + "preserveConstEnums": true, + "sourceMap": true, + "skipLibCheck": true, + "baseUrl": ".", + "typeRoots": [ + "./node_modules/@types" + ], + "lib": [ + "dom", + "es2015", + "es2016" + ] } } diff --git a/examples/with-typescript/tslint.json b/examples/with-typescript/tslint.json deleted file mode 100644 index 63f35ccc..00000000 --- a/examples/with-typescript/tslint.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": ["tslint:recommended"], - "jsRules": {}, - "rules": { - "quotemark": [true, "single", "jsx-double"], - "semicolon": [true, "never"] - }, - "rulesDirectory": [] -} From de6741f886178d8f9b4af6e109838642aebda1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Tegelund?= Date: Wed, 7 Feb 2018 17:09:23 +0900 Subject: [PATCH 09/64] Fix typo in withRouter (#3723) --- lib/router/with-router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/router/with-router.js b/lib/router/with-router.js index 1bca3f9d..91fceca8 100644 --- a/lib/router/with-router.js +++ b/lib/router/with-router.js @@ -11,7 +11,7 @@ export default function withRouter (ComposedComponent) { router: PropTypes.object } - static displayName = `withRoute(${displayName})` + static displayName = `withRouter(${displayName})` render () { const props = { From 955cc82736f6e1e3913a8d94d549e40e5cf296d0 Mon Sep 17 00:00:00 2001 From: Martin Groen Date: Wed, 7 Feb 2018 19:35:35 +1100 Subject: [PATCH 10/64] Serve static `/_next/static/*` relative from `dir` directory (#3722) --- server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.js b/server/index.js index 633c3d83..46b15b3a 100644 --- a/server/index.js +++ b/server/index.js @@ -241,7 +241,7 @@ export default class Server { }, '/_next/static/:path*': async (req, res, params) => { - const p = join(this.dist, 'static', ...(params.path || [])) + const p = join(this.dir, this.dist, 'static', ...(params.path || [])) await this.serveStatic(req, res, p) }, From d015bac65f8eb8a3d6acf21ddcbd4e717dcf37fa Mon Sep 17 00:00:00 2001 From: Scott Polhemus Date: Wed, 7 Feb 2018 05:54:07 -0500 Subject: [PATCH 11/64] Copy static assets from .next folder when exporting (#3719) * Copy .next/static directory when exporting * Use nextDir --- server/export.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/export.js b/server/export.js index 92f0aa43..0a88fa1b 100644 --- a/server/export.js +++ b/server/export.js @@ -49,6 +49,15 @@ export default async function (dir, options, configuration) { ) } + // Copy .next/static directory + if (existsSync(join(nextDir, 'static'))) { + log(' copying "static build" directory') + await cp( + join(nextDir, 'static'), + join(outDir, '_next', 'static') + ) + } + // Copy dynamic import chunks if (existsSync(join(nextDir, 'chunks'))) { log(' copying dynamic import chunks') From 3e1a70a30ab6c9c18eeca663e2281ee317bc844b Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Wed, 7 Feb 2018 17:08:20 +0100 Subject: [PATCH 12/64] Upgrade styled-jsx (#3725) --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4636afcb..f32a5c8c 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "resolve": "1.5.0", "send": "0.16.1", "strip-ansi": "3.0.1", - "styled-jsx": "2.2.3", + "styled-jsx": "2.2.4", "touch": "3.1.0", "uglifyjs-webpack-plugin": "1.1.6", "unfetch": "3.0.0", diff --git a/yarn.lock b/yarn.lock index 80f15799..88056a50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6361,9 +6361,9 @@ style-loader@^0.19.1: loader-utils "^1.0.2" schema-utils "^0.3.0" -styled-jsx@2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-2.2.3.tgz#9fe3df55c852388019c3bf4c026bcbf8b3e003de" +styled-jsx@2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-2.2.4.tgz#aac903fd0a4240c356af335261183aa8f8673075" dependencies: babel-plugin-syntax-jsx "6.18.0" babel-types "6.26.0" From d7941438dda713e758e7bcf991862b9e7128965e Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Wed, 7 Feb 2018 22:28:19 +0100 Subject: [PATCH 13/64] Release 5.0.1-canary.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f32a5c8c..1893c712 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "5.0.0", + "version": "5.0.1-canary.1", "description": "Minimalistic framework for server-rendered React applications", "main": "./dist/server/next.js", "license": "MIT", From efe9afb2be3059f97ac06b8fe105d8d9e30368a5 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Thu, 8 Feb 2018 14:21:01 +0530 Subject: [PATCH 14/64] Remove aliasing of react and react-dom (#3731) * Remove aliasing of react and react-dom We need that functionality, but React does it automatically. So, we don't need to do that. This also fixes #3711 otherwise we need to add a few more aliases. * Revert "Remove aliasing of react and react-dom" This reverts commit 929d9567bbdc3f369f13888e846e848a25c9c261. * Allow to import modules like 'react-dom/server'. We do this by doing an extact match for 'react' and 'react-dom' --- server/build/webpack.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/server/build/webpack.js b/server/build/webpack.js index c9c22f9f..a6c643bc 100644 --- a/server/build/webpack.js +++ b/server/build/webpack.js @@ -149,10 +149,15 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer ], alias: { next: nextDir, - // This bypasses React's check for production mode. Since we know it is in production this way. - // This allows us to exclude React from being uglified. Saving multiple seconds per build. - react: dev ? 'react/cjs/react.development.js' : 'react/cjs/react.production.min.js', - 'react-dom': dev ? 'react-dom/cjs/react-dom.development.js' : 'react-dom/cjs/react-dom.production.min.js' + // React already does something similar to this. + // But if the user has react-devtools, it'll throw an error showing that + // we haven't done dead code elimination (via uglifyjs). + // We purposly do not uglify React code to save the build time. + // (But it didn't increase the overall build size) + // Here we are doing an exact match with '$' + // So, you can still require nested modules like `react-dom/server` + react$: dev ? 'react/cjs/react.development.js' : 'react/cjs/react.production.min.js', + 'react-dom$': dev ? 'react-dom/cjs/react-dom.development.js' : 'react-dom/cjs/react-dom.production.min.js' } }, resolveLoader: { From 882efbbac3c4c86dbee40b271174cb2d77182649 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Thu, 8 Feb 2018 14:23:41 +0530 Subject: [PATCH 15/64] Release 5.0.1-canary.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1893c712..b32d2926 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "5.0.1-canary.1", + "version": "5.0.1-canary.2", "description": "Minimalistic framework for server-rendered React applications", "main": "./dist/server/next.js", "license": "MIT", From 34670bbd3dd86c5e72c09c6c086fbf9970f3ddd0 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Thu, 8 Feb 2018 11:35:06 +0100 Subject: [PATCH 16/64] Upgrade react-hot-loader (#3733) --- package.json | 2 +- yarn.lock | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b32d2926..611c510b 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "pkg-up": "2.0.0", "prop-types": "15.6.0", "prop-types-exact": "1.1.1", - "react-hot-loader": "4.0.0-beta.18", + "react-hot-loader": "4.0.0-beta.21", "recursive-copy": "2.0.6", "resolve": "1.5.0", "send": "0.16.1", diff --git a/yarn.lock b/yarn.lock index 88056a50..4a577dc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5563,16 +5563,17 @@ react-dom@16.2.0: object-assign "^4.1.1" prop-types "^15.6.0" -react-hot-loader@4.0.0-beta.18: - version "4.0.0-beta.18" - resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.0.0-beta.18.tgz#5a3d1b5bd813633380b88c0c660019dbf638975d" +react-hot-loader@4.0.0-beta.21: + version "4.0.0-beta.21" + resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.0.0-beta.21.tgz#46a0345621f1a03cb3c1c6dc130f153fa66e5bae" dependencies: fast-levenshtein "^2.0.6" global "^4.3.0" hoist-non-react-statics "^2.3.1" - react-stand-in "^4.0.0-beta.18" + prop-types "^15.6.0" + react-stand-in "^4.0.0-beta.21" -react-stand-in@^4.0.0-beta.18: +react-stand-in@^4.0.0-beta.21: version "4.0.0-beta.21" resolved "https://registry.yarnpkg.com/react-stand-in/-/react-stand-in-4.0.0-beta.21.tgz#fb694e465cb20fab7f36d3284f82b68bbd7a657e" dependencies: From 73bf0ab162b9f9eb214a2c09d83a022dd79888d9 Mon Sep 17 00:00:00 2001 From: Yevhen Uzhva Date: Fri, 9 Feb 2018 10:29:10 +0200 Subject: [PATCH 17/64] [Fix] with-global-stylesheet example (#3741) --- examples/with-global-stylesheet/package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/with-global-stylesheet/package.json b/examples/with-global-stylesheet/package.json index 72738a39..4166dea9 100644 --- a/examples/with-global-stylesheet/package.json +++ b/examples/with-global-stylesheet/package.json @@ -22,8 +22,5 @@ "react": "^16.0.0", "react-dom": "^16.0.0", "sass-loader": "^6.0.6" - }, - "devDependencies": { - "now": "^8.3.10" } } From 1b473e478d8d5888af9b9a405620bf60918f36e5 Mon Sep 17 00:00:00 2001 From: Tim Teeling Date: Fri, 9 Feb 2018 03:31:30 -0500 Subject: [PATCH 18/64] Fix with-polyfills example for Next 5 (#3740) --- examples/with-polyfills/next.config.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/with-polyfills/next.config.js b/examples/with-polyfills/next.config.js index eca79708..67527712 100644 --- a/examples/with-polyfills/next.config.js +++ b/examples/with-polyfills/next.config.js @@ -3,7 +3,11 @@ module.exports = { const originalEntry = cfg.entry cfg.entry = async () => { const entries = await originalEntry() - entries['main.js'].unshift('./client/polyfills.js') + + if (entries['main.js']) { + entries['main.js'].unshift('./client/polyfills.js') + } + return entries } From b8076a84a0c7e679406f0953b0a2f1edb063e96d Mon Sep 17 00:00:00 2001 From: astenmies <33988299+astenmies@users.noreply.github.com> Date: Fri, 9 Feb 2018 09:40:32 +0100 Subject: [PATCH 19/64] With DraftJS example (#3736) --- examples/with-draft-js/README.md | 41 ++++ examples/with-draft-js/package.json | 17 ++ examples/with-draft-js/pages/index.js | 270 ++++++++++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 examples/with-draft-js/README.md create mode 100644 examples/with-draft-js/package.json create mode 100644 examples/with-draft-js/pages/index.js diff --git a/examples/with-draft-js/README.md b/examples/with-draft-js/README.md new file mode 100644 index 00000000..5a9c90ac --- /dev/null +++ b/examples/with-draft-js/README.md @@ -0,0 +1,41 @@ +[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-draft-js) +# DraftJS Medium editor inspiration + +## How to use + +### Using `create-next-app` + +Download [`create-next-app`](https://github.com/segmentio/create-next-app) to bootstrap the example: + +``` +npm i -g create-next-app +create-next-app --example with-draft-js +``` + +### 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-draft-js +cd with-draft-js +``` + +Install it and run: + +```bash +npm install +npm run dev +``` + +Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)): + +```bash +now +``` + +## The idea behind the example + +Have you ever wanted to have an editor like medium.com in your Next.js app? DraftJS is avalaible for SSR, but some plugins like the toolbar are using `window`, which does not work when doing SSR. + +This example aims to provides a fully customizable example of the famous medium editor with DraftJS. The goal was to get it as customizable as possible, and fully working with Next.js without using the react-no-ssr package. diff --git a/examples/with-draft-js/package.json b/examples/with-draft-js/package.json new file mode 100644 index 00000000..fdb9eccd --- /dev/null +++ b/examples/with-draft-js/package.json @@ -0,0 +1,17 @@ +{ + "name": "with-draft-js", + "version": "1.0.0", + "author": "Asten Mies", + "license": "ISC", + "scripts": { + "dev": "next", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "draft-js": "0.10.5", + "next": "5.0.0", + "react": "16.2.0", + "react-dom": "16.2.0" + } +} diff --git a/examples/with-draft-js/pages/index.js b/examples/with-draft-js/pages/index.js new file mode 100644 index 00000000..286b3305 --- /dev/null +++ b/examples/with-draft-js/pages/index.js @@ -0,0 +1,270 @@ +import React from 'react' +import { + Editor, + EditorState, + RichUtils, + convertToRaw, + convertFromRaw + } from 'draft-js' + +export default class App extends React.Component { + constructor (props) { + super(props) + this.state = { + editorState: EditorState.createWithContent(convertFromRaw(initialData)), + showToolbar: false, + windowWidth: 0, + toolbarMeasures: { + w: 0, + h: 0 + }, + selectionMeasures: { + w: 0, + h: 0 + }, + selectionCoordinates: { + x: 0, + y: 0 + }, + toolbarCoordinates: { + x: 0, + y: 0 + }, + showRawData: false + } + + this.focus = () => this.editor.focus() + this.onChange = (editorState) => this.setState({editorState}) + } + + onClickEditor = () => { + this.focus() + this.checkSelectedText() + } + + // 1- Check if some text is selected + checkSelectedText = () => { + if (typeof window !== 'undefined') { + const text = window.getSelection().toString() + if (text !== '') { + // 1-a Define the selection coordinates + this.setSelectionXY() + } else { + // Hide the toolbar if nothing is selected + this.setState({ + showToolbar: false + }) + } + } + } + + // 2- Identify the selection coordinates + setSelectionXY = () => { + var r = window.getSelection().getRangeAt(0).getBoundingClientRect() + var relative = document.body.parentNode.getBoundingClientRect() + // 2-a Set the selection coordinates in the state + this.setState({ + selectionCoordinates: r, + windowWidth: relative.width, + selectionMeasures: { + w: r.width, + h: r.height + } + }, () => this.showToolbar()) + } + + // 3- Show the toolbar + showToolbar = () => { + this.setState({ + showToolbar: true + }, () => this.measureToolbar()) + } + + // 4- The toolbar was hidden until now + measureToolbar = () => { + // 4-a Define the toolbar width and height, as it is now visible + this.setState({ + toolbarMeasures: { + w: this.elemWidth, + h: this.elemHeight + } + }, () => this.setToolbarXY()) + } + + // 5- Now that we have all measures, define toolbar coordinates + setToolbarXY = () => { + let coordinates = {} + + const { selectionMeasures, selectionCoordinates, toolbarMeasures, windowWidth } = this.state + + const hiddenTop = selectionCoordinates.y < toolbarMeasures.h + const hiddenRight = windowWidth - selectionCoordinates.x < toolbarMeasures.w / 2 + const hiddenLeft = selectionCoordinates.x < toolbarMeasures.w / 2 + + const normalX = selectionCoordinates.x - (toolbarMeasures.w / 2) + (selectionMeasures.w / 2) + const normalY = selectionCoordinates.y - toolbarMeasures.h + + const invertedY = selectionCoordinates.y + selectionMeasures.h + const moveXToLeft = windowWidth - toolbarMeasures.w + const moveXToRight = 0 + + coordinates = { + x: normalX, + y: normalY + } + + if (hiddenTop) { + coordinates.y = invertedY + } + + if (hiddenRight) { + coordinates.x = moveXToLeft + } + + if (hiddenLeft) { + coordinates.x = moveXToRight + } + + this.setState({ + toolbarCoordinates: coordinates + }) + } + + handleKeyCommand = (command) => { + const {editorState} = this.state + const newState = RichUtils.handleKeyCommand(editorState, command) + if (newState) { + this.onChange(newState) + return true + } + return false + } + + toggleToolbar = (inlineStyle) => { + this.onChange( + RichUtils.toggleInlineStyle( + this.state.editorState, + inlineStyle + ) + ) + } + + render () { + const {editorState} = this.state + // Make sure we're not on the ssr + if (typeof window !== 'undefined') { + // Let's stick the toolbar to the selection + // when the window is resized + window.addEventListener('resize', this.checkSelectedText) + } + + const toolbarStyle = { + display: this.state.showToolbar ? 'block' : 'none', + backgroundColor: 'black', + color: 'white', + position: 'absolute', + left: this.state.toolbarCoordinates.x, + top: this.state.toolbarCoordinates.y, + zIndex: 999, + padding: 10 + } + return ( +
+
{ + this.elemWidth = elem ? elem.clientWidth : 0 + this.elemHeight = elem ? elem.clientHeight : 0 + }} + style={toolbarStyle} + > + +
+
+ { this.editor = element }} + /> +
+
+
+ {this.state.showRawData && JSON.stringify(convertToRaw(editorState.getCurrentContent()))} +
+
+ ) + } +} + +// Custom overrides for each style +const styleMap = { + CODE: { + backgroundColor: 'rgba(0, 0, 0, 0.05)', + fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace', + fontSize: 16, + padding: 4 + }, + BOLD: { + color: '#395296', + fontWeight: 'bold' + }, + ANYCUSTOMSTYLE: { + color: '#00e400' + } +} + +class ToolbarButton extends React.Component { + constructor () { + super() + this.onToggle = (e) => { + e.preventDefault() + this.props.onToggle(this.props.style) + } + } + + render () { + const buttonStyle = { + padding: 10 + } + return ( + + { this.props.label } + + ) + } +} + +var toolbarItems = [ + {label: 'Bold', style: 'BOLD'}, + {label: 'Italic', style: 'ITALIC'}, + {label: 'Underline', style: 'UNDERLINE'}, + {label: 'Code', style: 'CODE'}, + {label: 'Surprise', style: 'ANYCUSTOMSTYLE'} +] + +const ToolBar = (props) => { + var currentStyle = props.editorState.getCurrentInlineStyle() + return ( +
+ {toolbarItems.map(toolbarItem => + + )} +
+ ) +} + +const initialData = {'blocks': [{'key': '16d0k', 'text': 'You can edit this text.', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [{'offset': 0, 'length': 23, 'style': 'BOLD'}], 'entityRanges': [], 'data': {}}, {'key': '98peq', 'text': '', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [], 'entityRanges': [], 'data': {}}, {'key': 'ecmnc', 'text': 'Luke Skywalker has vanished. In his absence, the sinister FIRST ORDER has risen from the ashes of the Empire and will not rest until Skywalker, the last Jedi, has been destroyed.', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [{'offset': 0, 'length': 14, 'style': 'BOLD'}, {'offset': 133, 'length': 9, 'style': 'BOLD'}], 'entityRanges': [], 'data': {}}, {'key': 'fe2gn', 'text': '', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [], 'entityRanges': [], 'data': {}}, {'key': '4481k', 'text': 'With the support of the REPUBLIC, General Leia Organa leads a brave RESISTANCE. She is desperate to find her brother Luke and gain his help in restoring peace and justice to the galaxy.', 'type': 'unstyled', 'depth': 0, 'inlineStyleRanges': [{'offset': 34, 'length': 19, 'style': 'BOLD'}, {'offset': 117, 'length': 4, 'style': 'BOLD'}, {'offset': 68, 'length': 10, 'style': 'ANYCUSTOMSTYLE'}], 'entityRanges': [], 'data': {}}], 'entityMap': {}} From 2ba6a9aff7ad8fd1bf3df5326626f32f4d01a508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20M?= Date: Fri, 9 Feb 2018 15:41:09 +0100 Subject: [PATCH 20/64] Fix svg-components example and improve babel example file (#3746) --- examples/svg-components/package.json | 2 +- readme.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/svg-components/package.json b/examples/svg-components/package.json index 65f3541d..46e131f1 100644 --- a/examples/svg-components/package.json +++ b/examples/svg-components/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "next": "latest", - "react": "latest, + "react": "latest", "react-dom": "latest" }, "devDependencies": { diff --git a/readme.md b/readme.md index 60352fcf..3a148d44 100644 --- a/readme.md +++ b/readme.md @@ -1094,7 +1094,8 @@ Here's an example `.babelrc` file: ```json { - "presets": ["next/babel", "env"] + "presets": ["next/babel"], + "plugins": [] } ``` From 9a10461150f2cfdee83c0d427803f73c70541aba Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Fri, 9 Feb 2018 16:55:45 +0100 Subject: [PATCH 21/64] =?UTF-8?q?Don=E2=80=99t=20include=20script=20that?= =?UTF-8?q?=20we=20know=20is=20going=20to=20error=20(#3747)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don’t include script that we know is going to error * Add check to make sure page script is not included * Loop over script tags, cheerio fails on / --- server/document.js | 17 +++++++++++++---- server/render.js | 3 ++- test/integration/basic/test/rendering.js | 10 ++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/server/document.js b/server/document.js index 49eea8a5..6a27ffb2 100644 --- a/server/document.js +++ b/server/document.js @@ -84,12 +84,12 @@ export class Head extends Component { render () { const { head, styles, __NEXT_DATA__ } = this.context._documentProps - const { pathname, buildId, assetPrefix } = __NEXT_DATA__ + const { page, pathname, buildId, assetPrefix } = __NEXT_DATA__ const pagePathname = getPagePathname(pathname) return {(head || []).map((h, i) => React.cloneElement(h, { key: h.key || i }))} - + {page !== '_error' && } {this.getPreloadDynamicChunks()} {this.getPreloadMainLinks()} @@ -173,7 +173,7 @@ export class NextScript extends Component { render () { const { staticMarkup, __NEXT_DATA__, chunks } = this.context._documentProps - const { pathname, buildId, assetPrefix } = __NEXT_DATA__ + const { page, pathname, buildId, assetPrefix } = __NEXT_DATA__ const pagePathname = getPagePathname(pathname) __NEXT_DATA__.chunks = chunks.names @@ -193,9 +193,18 @@ export class NextScript extends Component { __NEXT_REGISTER_CHUNK = function (chunkName, fn) { __NEXT_LOADED_CHUNKS__.push({ chunkName: chunkName, fn: fn }) } + + ${page === '_error' && ` + __NEXT_REGISTER_PAGE('/index', function() { + var error = new Error('Page does not exist: ${htmlescape(pathname)}') + error.statusCode = 404 + + return { error: error } + }) + `} ` }} />} -