From a8a97b07c7990c14e80f5f7d40e38c367b67a22b Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Tue, 4 Sep 2018 16:01:50 +0200 Subject: [PATCH] Provide a way to copy files in exportPathMap (#5089) Related #4659 Adds the possibility for users to copy files inside of `exportPathMap`. This allows for adding `robots.txt` `sitemap.xml` etc. another use case is for https://github.com/hanford/next-offline, currently it's manually reading the buildId in `exportPathMap`. To allow users to do this we'll introduce a new parameter holding an object with the following keys: - `dev` - `true` when `exportPathMap` is being called in development. `false` when running `next export`. In development `exportPathMap` is used to define routes and behavior like copying files is not required. - `dir` - Absolute path to the project directory - `outDir` - Absolute path to the `out` directory (configurable with `-o` or `--outdir`). When `dev` is `true` the value of `outDir` will be `null`. - `distDir` - Absolute path to the `.next` directory (configurable using the `distDir` config key) - `buildId` - The buildId the export is running for Example usage: ```js // next.config.js const fs = require('fs') const {join} = require('path') const {promisify} = require('util') const copyFile = promisify(fs.copyFile) module.exports = { exportPathMap: async function (defaultPathMap, {dev, dir, outDir, distDir, buildId}) { if(dev) { return defaultPathMap } // This will copy robots.txt from your project root into the out directory await copyFile(join(dir, 'robots.txt'), join(outDir, 'robots.txt')) return defaultPathMap } } ``` --- README.md | 32 +++++++++++++++++++++++++++++++- export/index.js | 23 ++++++----------------- server/index.js | 2 +- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index c2fa84b6..7fa4a529 100644 --- a/README.md +++ b/README.md @@ -1479,7 +1479,7 @@ next build next export ``` -By default `next export` doesn't require any configuration. It will generate a default `exportPathMap` containing the routes to pages inside the `pages` directory. +By default `next export` doesn't require any configuration. It will generate a default `exportPathMap` containing the routes to pages inside the `pages` directory. This default mapping is available as `defaultPathMap` in the example below. If your application has dynamic routes you can add a dynamic `exportPathMap` in `next.config.js`. This function is asynchronous and gets the default `exportPathMap` as a parameter. @@ -1538,6 +1538,36 @@ For an example, simply visit the `out` directory and run following command to de now ``` +### Copying custom files + +In case you have to copy custom files like a robots.txt or generate a sitemap.xml you can do this inside of `exportPathMap`. +`exportPathMap` gets a few contextual parameter to aid you with creating/copying files: + +- `dev` - `true` when `exportPathMap` is being called in development. `false` when running `next export`. In development `exportPathMap` is used to define routes and behavior like copying files is not required. +- `dir` - Absolute path to the project directory +- `outDir` - Absolute path to the `out` directory (configurable with `-o` or `--outdir`). When `dev` is `true` the value of `outDir` will be `null`. +- `distDir` - Absolute path to the `.next` directory (configurable using the `distDir` config key) +- `buildId` - The buildId the export is running for + +```js +// next.config.js +const fs = require('fs') +const {join} = require('path') +const {promisify} = require('util') +const copyFile = promisify(fs.copyFile) + +module.exports = { + exportPathMap: async function (defaultPathMap, {dev, dir, outDir, distDir, buildId}) { + if (dev) { + return defaultPathMap + } + // This will copy robots.txt from your project root into the out directory + await copyFile(join(dir, 'robots.txt'), join(outDir, 'robots.txt')) + return defaultPathMap + } +} +``` + ### Limitation With `next export`, we build a HTML version of your app. At export time we will run `getInitialProps` of your pages. diff --git a/export/index.js b/export/index.js index eeffcab7..881767e6 100644 --- a/export/index.js +++ b/export/index.js @@ -10,6 +10,11 @@ import { setAssetPrefix } from '../lib/asset' import * as envConfig from '../lib/runtime-config' export default async function (dir, options, configuration) { + function log (message) { + if (options.silent) return + console.log(message) + } + dir = resolve(dir) const nextConfig = configuration || loadConfig(PHASE_EXPORT, dir) const distDir = join(dir, nextConfig.distDir) @@ -64,17 +69,6 @@ export default async function (dir, options, configuration) { ) } - // Copy dynamic import chunks - if (existsSync(join(distDir, 'chunks'))) { - log(' copying dynamic import chunks') - - await mkdirp(join(outDir, '_next', 'webpack')) - await cp( - join(distDir, 'chunks'), - join(outDir, '_next', 'webpack', 'chunks') - ) - } - // Get the exportPathMap from the config file if (typeof nextConfig.exportPathMap !== 'function') { console.log(`> No "exportPathMap" found in "${CONFIG_FILE}". Generating map from "./pages"`) @@ -114,7 +108,7 @@ export default async function (dir, options, configuration) { nextExport: true } - const exportPathMap = await nextConfig.exportPathMap(defaultPathMap) + const exportPathMap = await nextConfig.exportPathMap(defaultPathMap, {dev: false, dir, outDir, distDir, buildId}) const exportPaths = Object.keys(exportPathMap) for (const path of exportPaths) { @@ -146,9 +140,4 @@ export default async function (dir, options, configuration) { // Add an empty line to the console for the better readability. log('') - - function log (message) { - if (options.silent) return - console.log(message) - } } diff --git a/server/index.js b/server/index.js index 776e52e7..00dd512f 100644 --- a/server/index.js +++ b/server/index.js @@ -155,7 +155,7 @@ export default class Server { // So that the user doesn't have to define a custom server reading the exportPathMap if (this.dev && this.nextConfig.exportPathMap) { console.log('Defining routes from exportPathMap') - const exportPathMap = await this.nextConfig.exportPathMap({}) // In development we can't give a default path mapping + const exportPathMap = await this.nextConfig.exportPathMap({}, {dev: true, dir: this.dir, outDir: null, distDir: this.distDir, buildId: this.buildId}) // In development we can't give a default path mapping for (const path in exportPathMap) { const {page, query = {}} = exportPathMap[path] routes[path] = async (req, res, params, parsedUrl) => {