diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 2e779633de1af..4d15b37533f69 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -14,7 +14,8 @@ jobs: with: only-labels: 'please add a complete reproduction' close-issue-message: 'This issue has been automatically closed after 30 days of inactivity with no reproduction. If you are running into a similar issue, please open a new issue with a reproduction. Thank you.' - days-before-issue-close: 30 + days-before-issue-close: 1 + days-before-issue-stale: 30 days-before-pr-close: -1 days-before-pr-stale: -1 exempt-issue-labels: 'blocked,must,should,keep' diff --git a/docs/advanced-features/react-18.md b/docs/advanced-features/react-18.md index 60f7504837f55..1d82c82b71b47 100644 --- a/docs/advanced-features/react-18.md +++ b/docs/advanced-features/react-18.md @@ -12,6 +12,8 @@ Ensure you have the `rc` npm tag of React installed: npm install next@latest react@rc react-dom@rc ``` +That's all! You can now start using React 18's new APIs like `startTransition` and `Suspense` in Next.js. + ### Enable SSR Streaming (Alpha) Concurrent features in React 18 include built-in support for server-side Suspense and SSR streaming support, allowing you to server-render pages using HTTP streaming. diff --git a/docs/api-reference/next/link.md b/docs/api-reference/next/link.md index ba6b656b7aa9b..a7ad63ca27047 100644 --- a/docs/api-reference/next/link.md +++ b/docs/api-reference/next/link.md @@ -204,7 +204,7 @@ The default behavior of the `Link` component is to `push` a new URL into the `hi The default behavior of `Link` is to scroll to the top of the page. When there is a hash defined it will scroll to the specific id, like a normal `` tag. To prevent scrolling to the top / hash `scroll={false}` can be added to `Link`: ```jsx - + Disables scrolling to the top ``` diff --git a/docs/basic-features/eslint.md b/docs/basic-features/eslint.md index 5e50cc656c4fb..1ce00e9cdaf00 100644 --- a/docs/basic-features/eslint.md +++ b/docs/basic-features/eslint.md @@ -90,6 +90,7 @@ Next.js provides an ESLint plugin, [`eslint-plugin-next`](https://www.npmjs.com/ | ✔️ | [next/no-head-import-in-document](https://nextjs.org/docs/messages/no-head-import-in-document) | Disallow importing next/head in pages/document.js | | ✔️ | [next/no-html-link-for-pages](https://nextjs.org/docs/messages/no-html-link-for-pages) | Prohibit HTML anchor links to pages without a Link component | | ✔️ | [next/no-img-element](https://nextjs.org/docs/messages/no-img-element) | Prohibit usage of HTML <img> element | +| ✔️ | [next/no-head-element](https://nextjs.org/docs/messages/no-head-element) | Prohibit usage of HTML <head> element | | ✔️ | [next/no-page-custom-font](https://nextjs.org/docs/messages/no-page-custom-font) | Prevent page-only custom fonts | | ✔️ | [next/no-sync-scripts](https://nextjs.org/docs/messages/no-sync-scripts) | Forbid synchronous scripts | | ✔️ | [next/no-title-in-document-head](https://nextjs.org/docs/messages/no-title-in-document-head) | Disallow using <title> with Head from next/document | @@ -202,11 +203,15 @@ Then, add `prettier` to your existing ESLint config: If you would like to use `next lint` with [lint-staged](https://github.com/okonet/lint-staged) to run the linter on staged git files, you'll have to add the following to the `.lintstagedrc.js` file in the root of your project in order to specify usage of the `--file` flag. ```js +const path = require('path') + +const buildEslintCommand = (filenames) => + `next lint --fix --file ${filenames + .map((f) => path.relative(process.cwd(), f)) + .join(' --file ')}` + module.exports = { - '**/*.js?(x)': (filenames) => - `next lint --fix --file ${filenames - .map((file) => file.split(process.cwd())[1]) - .join(' --file ')}`, + '*.{js,jsx,ts,tsx}': [buildEslintCommand], } ``` diff --git a/errors/manifest.json b/errors/manifest.json index bb7a91f4a85c7..4aaeead555a5a 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -447,6 +447,7 @@ "path": "/errors/link-multiple-children.md" }, { "title": "no-img-element", "path": "/errors/no-img-element.md" }, + { "title": "no-head-element", "path": "/errors/no-head-element.md" }, { "title": "non-dynamic-getstaticpaths-usage", "path": "/errors/non-dynamic-getstaticpaths-usage.md" diff --git a/errors/no-head-element.md b/errors/no-head-element.md new file mode 100644 index 0000000000000..670894e64a445 --- /dev/null +++ b/errors/no-head-element.md @@ -0,0 +1,30 @@ +# No Head Element + +### Why This Error Occurred + +An HTML `
` element was used to include page-level metadata, but this can cause unexpected behavior in a Next.js application. Use Next.js' built-in `` component instead. + +### Possible Ways to Fix It + +Import and use the `
` component: + +```jsx +import Head from 'next/head' + +function Index() { + return ( + <> +
+
+
+
+ >
+ )
+}
+
+export default Index
+```
+
+### Useful Links
+
+- [next/head](https://nextjs.org/docs/api-reference/next/head)
diff --git a/examples/with-docker/Dockerfile b/examples/with-docker/Dockerfile
index 57e44c4e05ced..aa4b329d5e399 100644
--- a/examples/with-docker/Dockerfile
+++ b/examples/with-docker/Dockerfile
@@ -9,9 +9,9 @@ RUN yarn install --frozen-lockfile
# Rebuild the source code only when needed
FROM node:16-alpine AS builder
WORKDIR /app
-COPY . .
COPY --from=deps /app/node_modules ./node_modules
-RUN yarn build && yarn install --production --ignore-scripts --prefer-offline
+COPY . .
+RUN yarn build
# Production image, copy all the files and run next
FROM node:16-alpine AS runner
diff --git a/examples/with-segment-analytics/pages/_app.js b/examples/with-segment-analytics/pages/_app.js
index 402797f3fa0b1..5c2c2bf7d0a2d 100644
--- a/examples/with-segment-analytics/pages/_app.js
+++ b/examples/with-segment-analytics/pages/_app.js
@@ -24,7 +24,10 @@ function MyApp({ Component, pageProps }) {
return (
element', + category: 'HTML', + recommended: true, + url: 'https://nextjs.org/docs/messages/no-head-element', + }, + fixable: 'code', + }, + + create: function (context) { + return { + JSXOpeningElement(node) { + if (node.name.name !== 'head') { + return + } + + context.report({ + node, + message: `Do not use
. Use Head from 'next/head' instead. See: https://nextjs.org/docs/messages/no-head-element`,
+ })
+ },
+ }
+ },
+}
diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json
index 236bebf7b317f..90f4d1398f0b6 100644
--- a/packages/eslint-plugin-next/package.json
+++ b/packages/eslint-plugin-next/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/eslint-plugin-next",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"description": "ESLint plugin for NextJS.",
"main": "lib/index.js",
"license": "MIT",
diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json
index 2e1d8803d8846..6bc81b90abe5e 100644
--- a/packages/next-bundle-analyzer/package.json
+++ b/packages/next-bundle-analyzer/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/bundle-analyzer",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"main": "index.js",
"license": "MIT",
"repository": {
diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json
index 8c047f9cde7e6..0432bebf22ddf 100644
--- a/packages/next-codemod/package.json
+++ b/packages/next-codemod/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/codemod",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"license": "MIT",
"dependencies": {
"chalk": "4.1.0",
diff --git a/packages/next-env/package.json b/packages/next-env/package.json
index 3307a20722109..47a277a0aaaa1 100644
--- a/packages/next-env/package.json
+++ b/packages/next-env/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/env",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"keywords": [
"react",
"next",
diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json
index db8d4a55ec5a7..9e3157f7bd6da 100644
--- a/packages/next-mdx/package.json
+++ b/packages/next-mdx/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/mdx",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"main": "index.js",
"license": "MIT",
"repository": {
diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json
index 9fabe297f5388..6403a1dac08b5 100644
--- a/packages/next-plugin-storybook/package.json
+++ b/packages/next-plugin-storybook/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/plugin-storybook",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-storybook"
diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json
index 8eca7135ccc2e..867e70fecbae4 100644
--- a/packages/next-polyfill-module/package.json
+++ b/packages/next-polyfill-module/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-module",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)",
"main": "dist/polyfill-module.js",
"license": "MIT",
diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json
index a3ba50fb0edfe..0e73a87939b48 100644
--- a/packages/next-polyfill-nomodule/package.json
+++ b/packages/next-polyfill-nomodule/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-nomodule",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"description": "A polyfill for non-dead, nomodule browsers.",
"main": "dist/polyfill-nomodule.js",
"license": "MIT",
diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json
index 7f7ce4d19db3e..57df6bef350e8 100644
--- a/packages/next-swc/package.json
+++ b/packages/next-swc/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/swc",
- "version": "12.0.8-canary.13",
+ "version": "12.0.8-canary.14",
"private": true,
"scripts": {
"build-native": "napi build --platform --cargo-name next_swc_napi native",
diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts
index 46780e62b161b..262d0da81425b 100644
--- a/packages/next/build/entries.ts
+++ b/packages/next/build/entries.ts
@@ -36,6 +36,12 @@ export function createPagesMapping(
}
): PagesMapping {
const previousPages: PagesMapping = {}
+
+ // Do not process .d.ts files inside the `pages` folder
+ pagePaths = extensions.includes('ts')
+ ? pagePaths.filter((pagePath) => !pagePath.endsWith('.d.ts'))
+ : pagePaths
+
const pages: PagesMapping = pagePaths.reduce(
(result: PagesMapping, pagePath): PagesMapping => {
let page = pagePath.replace(
diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts
index 7c88a08164746..be3f4e04e8d33 100644
--- a/packages/next/build/index.ts
+++ b/packages/next/build/index.ts
@@ -5,7 +5,7 @@ import { isMatch } from 'next/dist/compiled/micromatch'
import { promises, writeFileSync } from 'fs'
import { Worker } from '../lib/worker'
import devalue from 'next/dist/compiled/devalue'
-import escapeStringRegexp from 'next/dist/compiled/escape-string-regexp'
+import { escapeStringRegexp } from '../shared/lib/escape-regexp'
import findUp from 'next/dist/compiled/find-up'
import { nanoid } from 'next/dist/compiled/nanoid/index.cjs'
import { pathToRegexp } from 'next/dist/compiled/path-to-regexp'
diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts
index f6c8c376bd940..37c918d622e4d 100644
--- a/packages/next/build/webpack-config.ts
+++ b/packages/next/build/webpack-config.ts
@@ -6,7 +6,7 @@ import semver from 'next/dist/compiled/semver'
import { webpack } from 'next/dist/compiled/webpack/webpack'
import type { webpack5 } from 'next/dist/compiled/webpack/webpack'
import path, { join as pathJoin, relative as relativePath } from 'path'
-import escapeRegExp from 'next/dist/compiled/escape-string-regexp'
+import { escapeStringRegexp } from '../shared/lib/escape-regexp'
import {
DOT_NEXT_ALIAS,
NEXT_PROJECT_ROOT,
@@ -1694,7 +1694,7 @@ export default async function getBaseWebpackConfig(
webpackConfig = await buildConfiguration(webpackConfig, {
supportedBrowsers,
rootDirectory: dir,
- customAppFile: new RegExp(escapeRegExp(path.join(pagesDir, `_app`))),
+ customAppFile: new RegExp(escapeStringRegexp(path.join(pagesDir, `_app`))),
isDevelopment: dev,
isServer,
webServerRuntime,
diff --git a/packages/next/build/webpack/config/blocks/css/index.ts b/packages/next/build/webpack/config/blocks/css/index.ts
index 61576638150f9..b44c4294591c2 100644
--- a/packages/next/build/webpack/config/blocks/css/index.ts
+++ b/packages/next/build/webpack/config/blocks/css/index.ts
@@ -139,7 +139,7 @@ export const css = curry(async function css(
...sassOptions
} = ctx.sassOptions
- const lazyPostCSSInitalizer = () =>
+ const lazyPostCSSInitializer = () =>
lazyPostCSS(
ctx.rootDirectory,
ctx.supportedBrowsers,
@@ -165,8 +165,9 @@ export const css = curry(async function css(
// To fix this, we use `resolve-url-loader` to rewrite the CSS
// imports to real file paths.
{
- loader: require.resolve('next/dist/compiled/resolve-url-loader'),
+ loader: require.resolve('../../../loaders/resolve-url-loader/index'),
options: {
+ postcss: lazyPostCSSInitializer,
// Source maps are not required here, but we may as well emit
// them.
sourceMap: true,
@@ -216,7 +217,7 @@ export const css = curry(async function css(
and: [ctx.rootDirectory],
not: [/node_modules/],
},
- use: getCssModuleLoader(ctx, lazyPostCSSInitalizer),
+ use: getCssModuleLoader(ctx, lazyPostCSSInitializer),
}),
],
})
@@ -241,7 +242,7 @@ export const css = curry(async function css(
},
use: getCssModuleLoader(
ctx,
- lazyPostCSSInitalizer,
+ lazyPostCSSInitializer,
sassPreprocessors
),
}),
@@ -303,7 +304,7 @@ export const css = curry(async function css(
and: [ctx.rootDirectory],
not: [/node_modules/],
},
- use: getGlobalCssLoader(ctx, lazyPostCSSInitalizer),
+ use: getGlobalCssLoader(ctx, lazyPostCSSInitializer),
}),
],
})
@@ -321,7 +322,7 @@ export const css = curry(async function css(
sideEffects: true,
test: regexCssGlobal,
issuer: { and: [ctx.customAppFile] },
- use: getGlobalCssLoader(ctx, lazyPostCSSInitalizer),
+ use: getGlobalCssLoader(ctx, lazyPostCSSInitializer),
}),
],
})
@@ -339,7 +340,7 @@ export const css = curry(async function css(
issuer: { and: [ctx.customAppFile] },
use: getGlobalCssLoader(
ctx,
- lazyPostCSSInitalizer,
+ lazyPostCSSInitializer,
sassPreprocessors
),
}),
diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/index.ts b/packages/next/build/webpack/loaders/next-serverless-loader/index.ts
index f61e1ecc2c8e3..fba18ce964f7a 100644
--- a/packages/next/build/webpack/loaders/next-serverless-loader/index.ts
+++ b/packages/next/build/webpack/loaders/next-serverless-loader/index.ts
@@ -1,10 +1,10 @@
import devalue from 'next/dist/compiled/devalue'
-import escapeRegexp from 'next/dist/compiled/escape-string-regexp'
import { join } from 'path'
import { parse } from 'querystring'
import { webpack } from 'next/dist/compiled/webpack/webpack'
import { API_ROUTE } from '../../../../lib/constants'
import { isDynamicRoute } from '../../../../shared/lib/router/utils'
+import { escapeStringRegexp } from '../../../../shared/lib/escape-regexp'
import { __ApiPreviewProps } from '../../../../server/api-utils'
import {
BUILD_MANIFEST,
@@ -62,7 +62,7 @@ const nextServerlessLoader: webpack.loader.Loader = function () {
)
const routesManifest = join(distDir, ROUTES_MANIFEST).replace(/\\/g, '/')
- const escapedBuildId = escapeRegexp(buildId)
+ const escapedBuildId = escapeStringRegexp(buildId)
const pageIsDynamicRoute = isDynamicRoute(page)
const encodedPreviewProps = devalue(
diff --git a/packages/next/build/webpack/loaders/resolve-url-loader/index.js b/packages/next/build/webpack/loaders/resolve-url-loader/index.js
new file mode 100644
index 0000000000000..4c1bc2522d896
--- /dev/null
+++ b/packages/next/build/webpack/loaders/resolve-url-loader/index.js
@@ -0,0 +1,103 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Ben Holloway
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import { SourceMapConsumer } from 'next/dist/compiled/source-map'
+import valueProcessor from './lib/value-processor'
+import { defaultJoin } from './lib/join-function'
+import process from './lib/postcss'
+/**
+ * A webpack loader that resolves absolute url() paths relative to their original source file.
+ * Requires source-maps to do any meaningful work.
+ * @param {string} content Css content
+ * @param {object} sourceMap The source-map
+ * @returns {string|String}
+ */
+export default async function resolveUrlLoader(content, sourceMap) {
+ const options = Object.assign(
+ {
+ sourceMap: this.sourceMap,
+ silent: false,
+ absolute: false,
+ keepQuery: false,
+ root: false,
+ debug: false,
+ join: defaultJoin,
+ },
+ this.getOptions()
+ )
+
+ let sourceMapConsumer
+ if (sourceMap) {
+ sourceMapConsumer = new SourceMapConsumer(sourceMap)
+ }
+
+ const callback = this.async()
+ const { postcss } = await options.postcss()
+ process(postcss, this.resourcePath, content, {
+ outputSourceMap: Boolean(options.sourceMap),
+ transformDeclaration: valueProcessor(this.resourcePath, options),
+ inputSourceMap: sourceMap,
+ sourceMapConsumer: sourceMapConsumer,
+ })
+ .catch(onFailure)
+ .then(onSuccess)
+
+ function onFailure(error) {
+ callback(encodeError('CSS error', error))
+ }
+
+ function onSuccess(reworked) {
+ if (reworked) {
+ // complete with source-map
+ // source-map sources are relative to the file being processed
+ if (options.sourceMap) {
+ callback(null, reworked.content, reworked.map)
+ }
+ // complete without source-map
+ else {
+ callback(null, reworked.content)
+ }
+ }
+ }
+
+ function encodeError(label, exception) {
+ return new Error(
+ [
+ 'resolve-url-loader',
+ ': ',
+ [label]
+ .concat(
+ (typeof exception === 'string' && exception) ||
+ (exception instanceof Error && [
+ exception.message,
+ exception.stack.split('\n')[1].trim(),
+ ]) ||
+ []
+ )
+ .filter(Boolean)
+ .join('\n '),
+ ].join('')
+ )
+ }
+}
diff --git a/packages/next/build/webpack/loaders/resolve-url-loader/lib/file-protocol.js b/packages/next/build/webpack/loaders/resolve-url-loader/lib/file-protocol.js
new file mode 100644
index 0000000000000..dc2c08de86b4f
--- /dev/null
+++ b/packages/next/build/webpack/loaders/resolve-url-loader/lib/file-protocol.js
@@ -0,0 +1,65 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Ben Holloway
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * Prepend file:// protocol to source path string or source-map sources.
+ */
+function prepend(candidate) {
+ if (typeof candidate === 'string') {
+ return 'file://' + candidate
+ } else if (
+ candidate &&
+ typeof candidate === 'object' &&
+ Array.isArray(candidate.sources)
+ ) {
+ return Object.assign({}, candidate, {
+ sources: candidate.sources.map(prepend),
+ })
+ } else {
+ throw new Error('expected string|object')
+ }
+}
+
+exports.prepend = prepend
+
+/**
+ * Remove file:// protocol from source path string or source-map sources.
+ */
+function remove(candidate) {
+ if (typeof candidate === 'string') {
+ return candidate.replace(/^file:\/{2}/, '')
+ } else if (
+ candidate &&
+ typeof candidate === 'object' &&
+ Array.isArray(candidate.sources)
+ ) {
+ return Object.assign({}, candidate, {
+ sources: candidate.sources.map(remove),
+ })
+ } else {
+ throw new Error('expected string|object')
+ }
+}
+
+exports.remove = remove
diff --git a/packages/next/build/webpack/loaders/resolve-url-loader/lib/join-function.js b/packages/next/build/webpack/loaders/resolve-url-loader/lib/join-function.js
new file mode 100644
index 0000000000000..b57d3c2df5582
--- /dev/null
+++ b/packages/next/build/webpack/loaders/resolve-url-loader/lib/join-function.js
@@ -0,0 +1,233 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Ben Holloway
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+import path from 'path'
+import fs from 'fs'
+
+const compose =
+ (f, g) =>
+ (...args) =>
+ f(g(...args))
+
+const simpleJoin = compose(path.normalize, path.join)
+
+/**
+ * The default join function iterates over possible base paths until a suitable join is found.
+ *
+ * The first base path is used as fallback for the case where none of the base paths can locate the actual file.
+ *
+ * @type {function}
+ */
+export const defaultJoin = createJoinForPredicate(function predicate(
+ _,
+ uri,
+ base,
+ i,
+ next
+) {
+ const absolute = simpleJoin(base, uri)
+ return fs.existsSync(absolute) ? absolute : next(i === 0 ? absolute : null)
+},
+'defaultJoin')
+
+function* createIterator(arr) {
+ for (const i of arr) {
+ yield i
+ }
+}
+
+/**
+ * Define a join function by a predicate that tests possible base paths from an iterator.
+ *
+ * The `predicate` is of the form:
+ *
+ * ```
+ * function(filename, uri, base, i, next):string|null
+ * ```
+ *
+ * Given the uri and base it should either return:
+ * - an absolute path success
+ * - a call to `next(null)` as failure
+ * - a call to `next(absolute)` where absolute is placeholder and the iterator continues
+ *
+ * The value given to `next(...)` is only used if success does not eventually occur.
+ *
+ * The `file` value is typically unused but useful if you would like to differentiate behaviour.
+ *
+ * You can write a much simpler function than this if you have specific requirements.
+ *
+ * @param {function} predicate A function that tests values
+ * @param {string} [name] Optional name for the resulting join function
+ */
+function createJoinForPredicate(predicate, name) {
+ /**
+ * A factory for a join function with logging.
+ *
+ * @param {string} filename The current file being processed
+ * @param {{debug:function|boolean,root:string}} options An options hash
+ */
+ function join(filename, options) {
+ const log = createDebugLogger(options.debug)
+
+ /**
+ * Join function proper.
+ *
+ * For absolute uri only `uri` will be provided. In this case we substitute any `root` given in options.
+ *
+ * @param {string} uri A uri path, relative or absolute
+ * @param {string|Iterator. {message}About Bypassed Page
+