diff --git a/packages/docusaurus-init/templates/bootstrap/package.json b/packages/docusaurus-init/templates/bootstrap/package.json
index 1e9bc1f3cd..71c2f90867 100644
--- a/packages/docusaurus-init/templates/bootstrap/package.json
+++ b/packages/docusaurus-init/templates/bootstrap/package.json
@@ -6,7 +6,8 @@
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
- "deploy": "docusaurus deploy"
+ "deploy": "docusaurus deploy",
+ "serve": "docusaurus serve"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-alpha.58",
diff --git a/packages/docusaurus-init/templates/classic/package.json b/packages/docusaurus-init/templates/classic/package.json
index 078cc826ed..1e00fbfa67 100644
--- a/packages/docusaurus-init/templates/classic/package.json
+++ b/packages/docusaurus-init/templates/classic/package.json
@@ -6,7 +6,8 @@
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
- "deploy": "docusaurus deploy"
+ "deploy": "docusaurus deploy",
+ "serve": "docusaurus serve"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-alpha.58",
diff --git a/packages/docusaurus-init/templates/facebook/package.json b/packages/docusaurus-init/templates/facebook/package.json
index 6b6ec0019a..d79d724685 100644
--- a/packages/docusaurus-init/templates/facebook/package.json
+++ b/packages/docusaurus-init/templates/facebook/package.json
@@ -7,6 +7,7 @@
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
+ "serve": "docusaurus serve",
"ci": "yarn lint && yarn prettier:diff",
"lint": "eslint --cache \"**/*.js\" && stylelint \"**/*.css\"",
"prettier": "prettier --config .prettierrc --write \"**/*.{js,md}\"",
diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js
index 5678d3bc9e..e19917b31c 100755
--- a/packages/docusaurus/bin/docusaurus.js
+++ b/packages/docusaurus/bin/docusaurus.js
@@ -11,7 +11,14 @@ const chalk = require('chalk');
const semver = require('semver');
const path = require('path');
const cli = require('commander');
-const {build, swizzle, deploy, start, externalCommand} = require('../lib');
+const {
+ build,
+ swizzle,
+ deploy,
+ start,
+ externalCommand,
+ serve,
+} = require('../lib');
const requiredVersion = require('../package.json').engines.node;
const pkg = require('../package.json');
const updateNotifier = require('update-notifier');
@@ -149,6 +156,35 @@ cli
});
});
+cli
+ .command('serve [siteDir]')
+ .description('Serve website')
+ .option(
+ '--dir
',
+ 'The full path for the new output directory, relative to the current workspace (default: build).',
+ )
+ .option('-p, --port ', 'use specified port (default: 3000)')
+ .option('--build', 'Build website before serving (default: false)')
+ .option('-h, --host ', 'use specified host (default: localhost')
+ .action(
+ (
+ siteDir = '.',
+ {
+ dir = 'build',
+ port = 3000,
+ host = 'localhost',
+ build: buildSite = false,
+ },
+ ) => {
+ wrapCommand(serve)(path.resolve(siteDir), {
+ dir,
+ port,
+ build: buildSite,
+ host,
+ });
+ },
+ );
+
cli.arguments('').action((cmd) => {
cli.outputHelp();
console.log(` ${chalk.red(`\n Unknown command ${chalk.yellow(cmd)}.`)}`);
diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json
index e3a6f30a29..bff4fd3c09 100644
--- a/packages/docusaurus/package.json
+++ b/packages/docusaurus/package.json
@@ -91,6 +91,7 @@
"react-router-config": "^5.1.1",
"react-router-dom": "^5.1.2",
"semver": "^6.3.0",
+ "serve-handler": "^6.1.3",
"shelljs": "^0.8.4",
"std-env": "^2.2.1",
"terser-webpack-plugin": "^2.3.5",
diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts
index d1ab306456..a2aeceed00 100644
--- a/packages/docusaurus/src/commands/build.ts
+++ b/packages/docusaurus/src/commands/build.ts
@@ -128,7 +128,9 @@ export default async function build(
console.log(
`\n${chalk.green('Success!')} Generated static files in ${chalk.cyan(
relativeDir,
- )}.\n`,
+ )}.Use ${chalk.greenBright(
+ '`npm run serve`',
+ )} to test your build locally.\n`,
);
if (forceTerminate && !cliOptions.bundleAnalyzer) {
process.exit(0);
diff --git a/packages/docusaurus/src/commands/serve.ts b/packages/docusaurus/src/commands/serve.ts
new file mode 100644
index 0000000000..6c6c4ba87a
--- /dev/null
+++ b/packages/docusaurus/src/commands/serve.ts
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import http from 'http';
+import serveHandler from 'serve-handler';
+import boxen from 'boxen';
+import chalk from 'chalk';
+import path from 'path';
+
+import build from './build';
+import choosePort from '../choosePort';
+
+export default async function serve(
+ siteDir: string,
+ cliOptions: {port: number; build: boolean; dir: string; host: string},
+): Promise {
+ let dir = path.join(siteDir, cliOptions.dir);
+ if (cliOptions.build) {
+ dir = await build(
+ siteDir,
+ {
+ outDir: dir,
+ },
+ false,
+ );
+ }
+ const port = await choosePort(cliOptions.host, cliOptions.port);
+ const server = http.createServer((req, res) => {
+ serveHandler(req, res, {
+ cleanUrls: true,
+ public: dir,
+ });
+ });
+ console.log(
+ boxen(
+ `${chalk.green(`Serving ${cliOptions.dir}!`)}\n\n- Local: http://${
+ cliOptions.host
+ }:${port}`,
+ {
+ borderColor: 'green',
+ padding: 1,
+ margin: 1,
+ align: 'center',
+ },
+ ),
+ );
+ server.listen(port);
+}
diff --git a/packages/docusaurus/src/index.ts b/packages/docusaurus/src/index.ts
index f6a0efa902..455d5a42bd 100644
--- a/packages/docusaurus/src/index.ts
+++ b/packages/docusaurus/src/index.ts
@@ -10,3 +10,4 @@ export {default as start} from './commands/start';
export {default as swizzle} from './commands/swizzle';
export {default as deploy} from './commands/deploy';
export {default as externalCommand} from './commands/external';
+export {default as serve} from './commands/serve';
diff --git a/website/docs/cli.md b/website/docs/cli.md
index ec40208547..b72e165bf9 100644
--- a/website/docs/cli.md
+++ b/website/docs/cli.md
@@ -111,3 +111,14 @@ Deploys your site with [GitHub Pages](https://pages.github.com/). Check out the
| --- | --- | --- |
| `--out-dir` | `build` | The full path for the new output directory, relative to the current workspace. |
| `--skip-build` | `false` | Deploy website without building it. This may be useful when using custom deploy script. |
+
+### `docusaurus serve`
+
+Serve your built website localy.
+
+| Name | Default | Description |
+| --- | --- | --- |
+| `--port` | `3000` | Use specified port |
+| `--dir` | `build` | The full path for the output directory, relative to the current workspace |
+| `--build` | `false` | Build website before serving |
+| `--host` | `localhost` | Specify a host to use. For example, if you want your server to be accessible externally, you can use `--host 0.0.0.0`. |
diff --git a/website/docs/deployment.md b/website/docs/deployment.md
index 34ebd739de..bfcdb621dd 100644
--- a/website/docs/deployment.md
+++ b/website/docs/deployment.md
@@ -13,6 +13,29 @@ Once it finishes, the static files will be generated within the `build/` directo
You can deploy your site to static site hosting services such as [Vercel](https://vercel.com/), [GitHub Pages](https://pages.github.com/), [Netlify](https://www.netlify.com/), [Render](https://render.com/static-sites), and [Surge](https://surge.sh/help/getting-started-with-surge). Docusaurus sites are statically rendered so they work without JavaScript too!
+## Testing Build Local
+
+It is important to test build before deploying to a production. Docusaurus includes a [`docusaurus serve`](cli.md#docusaurus-serve) command to test build localy.
+
+```bash npm2yarn
+npm run serve
+```
+
+## Self Hosting
+
+:::warning
+
+It is not the most performant solution
+
+:::
+
+Docusaurus can be self hosted using [`docusaurus serve`](cli.md#docusaurus-serve). Change port using `--port` and `--host` to change host.
+
+```bash npm2yarn
+npm run serve --build --port 80 --host 0.0.0.0
+
+```
+
## Deploying to GitHub Pages
Docusaurus provides an easy way to publish to [GitHub Pages](https://pages.github.com/). Which is hosting that comes for free with every GitHub repository.
diff --git a/yarn.lock b/yarn.lock
index 3fdec62475..5d68c85670 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -16476,7 +16476,7 @@ serialize-javascript@^2.1.2:
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==
-serve-handler@6.1.3:
+serve-handler@6.1.3, serve-handler@^6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8"
integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==