diff --git a/demos/base-path/.eslintrc b/demos/base-path/.eslintrc new file mode 100644 index 0000000000..abd5579b49 --- /dev/null +++ b/demos/base-path/.eslintrc @@ -0,0 +1,4 @@ +{ + "extends": "next", + "root": true +} diff --git a/demos/base-path/.gitignore b/demos/base-path/.gitignore new file mode 100644 index 0000000000..1437c53f70 --- /dev/null +++ b/demos/base-path/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/demos/base-path/README.md b/demos/base-path/README.md new file mode 100644 index 0000000000..b12f3e33e7 --- /dev/null +++ b/demos/base-path/README.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/demos/base-path/local-plugin/index.js b/demos/base-path/local-plugin/index.js new file mode 100644 index 0000000000..9e852e382e --- /dev/null +++ b/demos/base-path/local-plugin/index.js @@ -0,0 +1 @@ +module.exports = require('../../../lib') diff --git a/demos/base-path/local-plugin/manifest.yml b/demos/base-path/local-plugin/manifest.yml new file mode 100644 index 0000000000..7091f91411 --- /dev/null +++ b/demos/base-path/local-plugin/manifest.yml @@ -0,0 +1 @@ +name: '@netlify/plugin-nextjs-local' diff --git a/demos/base-path/local-plugin/package-lock.json b/demos/base-path/local-plugin/package-lock.json new file mode 100644 index 0000000000..11a2e6611e --- /dev/null +++ b/demos/base-path/local-plugin/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "local-plugin", + "version": "1.0.0", + "lockfileVersion": 1 +} diff --git a/demos/base-path/local-plugin/package.json b/demos/base-path/local-plugin/package.json new file mode 100644 index 0000000000..d692119a5b --- /dev/null +++ b/demos/base-path/local-plugin/package.json @@ -0,0 +1,11 @@ +{ + "name": "local-plugin", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "preinstall": "cd ../../.. && npm i" + }, + "author": "", + "license": "ISC" +} diff --git a/demos/base-path/netlify.toml b/demos/base-path/netlify.toml new file mode 100644 index 0000000000..ce3073f46f --- /dev/null +++ b/demos/base-path/netlify.toml @@ -0,0 +1,21 @@ +[build] +command = "next build" +publish = ".next" +ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ../../" + +[build.environment] +# cache Cypress binary in local "node_modules" folder +# so Netlify caches it +CYPRESS_CACHE_FOLDER = "../node_modules/.CypressBinary" + +[dev] +framework = "#static" + +[[plugins]] +package = "./local-plugin" + +[[plugins]] +package = "@netlify/plugin-local-install-core" + +# [[context.deploy-preview.plugins]] +# package = "netlify-plugin-cypress" \ No newline at end of file diff --git a/demos/base-path/next.config.js b/demos/base-path/next.config.js new file mode 100644 index 0000000000..22e35d86d1 --- /dev/null +++ b/demos/base-path/next.config.js @@ -0,0 +1,46 @@ +module.exports = { + // Configurable site features we support: + // distDir: 'build', + generateBuildId: () => 'build-id', + i18n: { + defaultLocale: 'en', + locales: ['en', 'es', 'fr'], + }, + async headers() { + return [ + { + source: '/', + headers: [ + { + key: 'x-custom-header', + value: 'my custom header value', + }, + ], + }, + ] + }, + trailingSlash: true, + // Configurable site features _to_ support: + basePath: '/docs', + // Rewrites allow you to map an incoming request path to a different destination path. + async rewrites() { + return { + beforeFiles: [ + { + source: '/old/:path*', + destination: '/:path*', + }, + ], + } + }, + // Redirects allow you to redirect an incoming request path to a different destination path. + async redirects() { + return [ + { + source: '/redirectme', + destination: '/', + permanent: true, + }, + ] + }, +} diff --git a/demos/base-path/pages/_app.js b/demos/base-path/pages/_app.js new file mode 100644 index 0000000000..1e1cec9242 --- /dev/null +++ b/demos/base-path/pages/_app.js @@ -0,0 +1,7 @@ +import '../styles/globals.css' + +function MyApp({ Component, pageProps }) { + return +} + +export default MyApp diff --git a/demos/base-path/pages/another.js b/demos/base-path/pages/another.js new file mode 100644 index 0000000000..e9cebb8bfd --- /dev/null +++ b/demos/base-path/pages/another.js @@ -0,0 +1,22 @@ +import Head from 'next/head' +import { useRouter } from 'next/router' +import styles from '../styles/Home.module.css' + +export default function Home() { + const { locale } = useRouter() + return ( +
+ + Create Next App + + + + +
+

Another page

+
+ + +
+ ) +} diff --git a/demos/base-path/pages/api/hello.js b/demos/base-path/pages/api/hello.js new file mode 100644 index 0000000000..9987aff4c3 --- /dev/null +++ b/demos/base-path/pages/api/hello.js @@ -0,0 +1,5 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction + +export default (req, res) => { + res.status(200).json({ name: 'John Doe' }) +} diff --git a/demos/base-path/pages/dynamic/[dynamic]/index.js b/demos/base-path/pages/dynamic/[dynamic]/index.js new file mode 100644 index 0000000000..6bc47a52e2 --- /dev/null +++ b/demos/base-path/pages/dynamic/[dynamic]/index.js @@ -0,0 +1,13 @@ +function Dynamic() { + return
This is the page with a dynamic path.
+} + +export const getStaticPaths = async () => { + return { paths: ['/dynamic/dynamic-1'], fallback: false } +} + +export const getStaticProps = ({ params }) => { + return { props: params } +} + +export default Dynamic diff --git a/demos/base-path/pages/fallback/[dynamic]/index.js b/demos/base-path/pages/fallback/[dynamic]/index.js new file mode 100644 index 0000000000..d89a0d6e70 --- /dev/null +++ b/demos/base-path/pages/fallback/[dynamic]/index.js @@ -0,0 +1,13 @@ +function Dynamic() { + return
This is the page with a dynamic path.
+} + +export const getStaticPaths = async () => { + return { paths: ['/fallback/dynamic-1'], fallback: 'blocking' } +} + +export const getStaticProps = ({ params }) => { + return { props: params } +} + +export default Dynamic diff --git a/demos/base-path/pages/index.js b/demos/base-path/pages/index.js new file mode 100644 index 0000000000..e9432c9c49 --- /dev/null +++ b/demos/base-path/pages/index.js @@ -0,0 +1,37 @@ +import Head from 'next/head' +import Link from 'next/link' +import { useRouter } from 'next/router' +import styles from '../styles/Home.module.css' + +export default function Home() { + const { locale } = useRouter() + return ( +
+ + Create Next App + + + + +
+

+ Welcome to Next.js! +

+ +

The current locale is {locale}

+ +
+ +

Dynamic Page

+ + +

Fallback Page

+ + +

Redirect me

+ +
+
+
+ ) +} diff --git a/demos/base-path/public/favicon.ico b/demos/base-path/public/favicon.ico new file mode 100644 index 0000000000..4965832f2c Binary files /dev/null and b/demos/base-path/public/favicon.ico differ diff --git a/demos/base-path/styles/Home.module.css b/demos/base-path/styles/Home.module.css new file mode 100644 index 0000000000..35454bb748 --- /dev/null +++ b/demos/base-path/styles/Home.module.css @@ -0,0 +1,121 @@ +.container { + min-height: 100vh; + padding: 0 0.5rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; +} + +.main { + padding: 5rem 0; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.footer { + width: 100%; + height: 100px; + border-top: 1px solid #eaeaea; + display: flex; + justify-content: center; + align-items: center; +} + +.footer a { + display: flex; + justify-content: center; + align-items: center; + flex-grow: 1; +} + +.title a { + color: #0070f3; + text-decoration: none; +} + +.title a:hover, +.title a:focus, +.title a:active { + text-decoration: underline; +} + +.title { + margin: 0; + line-height: 1.15; + font-size: 4rem; +} + +.title, +.description { + text-align: center; +} + +.description { + line-height: 1.5; + font-size: 1.5rem; +} + +.code { + background: #fafafa; + border-radius: 5px; + padding: 0.75rem; + font-size: 1.1rem; + font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, + Bitstream Vera Sans Mono, Courier New, monospace; +} + +.grid { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + max-width: 800px; + margin-top: 3rem; +} + +.card { + margin: 1rem; + padding: 1.5rem; + text-align: left; + color: inherit; + text-decoration: none; + border: 1px solid #eaeaea; + border-radius: 10px; + transition: color 0.15s ease, border-color 0.15s ease; + width: 45%; +} + +.card:hover, +.card:focus, +.card:active { + color: #0070f3; + border-color: #0070f3; +} + +.card h2 { + margin: 0 0 1rem 0; + font-size: 1.5rem; +} + +.card p { + margin: 0; + font-size: 1.25rem; + line-height: 1.5; +} + +.logo { + height: 1em; + margin-left: 0.5rem; +} + +@media (max-width: 600px) { + .grid { + width: 100%; + flex-direction: column; + } +} diff --git a/demos/base-path/styles/globals.css b/demos/base-path/styles/globals.css new file mode 100644 index 0000000000..e5e2dcc23b --- /dev/null +++ b/demos/base-path/styles/globals.css @@ -0,0 +1,16 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +a { + color: inherit; + text-decoration: none; +} + +* { + box-sizing: border-box; +} diff --git a/src/helpers/files.ts b/src/helpers/files.ts index 757f5abca1..4e58e7e2d8 100644 --- a/src/helpers/files.ts +++ b/src/helpers/files.ts @@ -73,10 +73,12 @@ export const moveStaticPages = async ({ netlifyConfig, target, i18n, + basePath, }: { netlifyConfig: NetlifyConfig target: 'server' | 'serverless' | 'experimental-serverless-trace' i18n: NextConfig['i18n'] + basePath?: string }): Promise => { console.log('Moving static page files to serve from CDN...') const outputDir = join(netlifyConfig.build.publish, target === 'server' ? 'server' : 'serverless') @@ -117,11 +119,12 @@ export const moveStaticPages = async ({ const isData = file.endsWith('.json') const source = join(root, file) const targetFile = isData ? join(dataDir, file) : file + const targetPath = basePath ? join(basePath, targetFile) : targetFile files.push(file) - filesManifest[file] = targetFile + filesManifest[file] = targetPath - const dest = join(netlifyConfig.build.publish, targetFile) + const dest = join(netlifyConfig.build.publish, targetPath) try { await move(source, dest) @@ -238,22 +241,23 @@ export const moveStaticPages = async ({ await writeJson(join(netlifyConfig.build.publish, 'static-manifest.json'), Object.entries(filesManifest)) if (i18n?.defaultLocale) { + const rootPath = basePath ? join(netlifyConfig.build.publish, basePath) : netlifyConfig.build.publish // Copy the default locale into the root - const defaultLocaleDir = join(netlifyConfig.build.publish, i18n.defaultLocale) + const defaultLocaleDir = join(rootPath, i18n.defaultLocale) if (existsSync(defaultLocaleDir)) { - await copy(defaultLocaleDir, `${netlifyConfig.build.publish}/`) + await copy(defaultLocaleDir, `${rootPath}/`) } - const defaultLocaleIndex = join(netlifyConfig.build.publish, `${i18n.defaultLocale}.html`) - const indexHtml = join(netlifyConfig.build.publish, 'index.html') + const defaultLocaleIndex = join(rootPath, `${i18n.defaultLocale}.html`) + const indexHtml = join(rootPath, 'index.html') if (existsSync(defaultLocaleIndex) && !existsSync(indexHtml)) { - try { - await copy(defaultLocaleIndex, indexHtml, { overwrite: false }) - await copy( - join(netlifyConfig.build.publish, `${i18n.defaultLocale}.json`), - join(netlifyConfig.build.publish, 'index.json'), - { overwrite: false }, - ) - } catch {} + await copy(defaultLocaleIndex, indexHtml, { overwrite: false }).catch(() => { + /* ignore */ + }) + await copy(join(rootPath, `${i18n.defaultLocale}.json`), join(rootPath, 'index.json'), { + overwrite: false, + }).catch(() => { + /* ignore */ + }) } } diff --git a/src/index.ts b/src/index.ts index 680f3e8583..fede662fb3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -85,7 +85,7 @@ const plugin: NetlifyPlugin = { } if (!process.env.SERVE_STATIC_FILES_FROM_ORIGIN) { - await moveStaticPages({ target, netlifyConfig, i18n }) + await moveStaticPages({ target, netlifyConfig, i18n, basePath }) } await generateStaticRedirects({