2022-04-27 11:50:29 +02:00
|
|
|
import type { ClientPagesLoaderOptions } from './webpack/loaders/next-client-pages-loader'
|
|
|
|
import type { MiddlewareLoaderOptions } from './webpack/loaders/next-middleware-loader'
|
2022-06-26 23:01:26 +02:00
|
|
|
import type { EdgeSSRLoaderQuery } from './webpack/loaders/next-edge-ssr-loader'
|
2022-05-20 14:24:00 +02:00
|
|
|
import type { NextConfigComplete } from '../server/config-shared'
|
2022-04-27 11:50:29 +02:00
|
|
|
import type { ServerlessLoaderQuery } from './webpack/loaders/next-serverless-loader'
|
2022-08-16 11:55:37 +02:00
|
|
|
import type { webpack } from 'next/dist/compiled/webpack/webpack'
|
feat(next): Support has match and locale option on middleware config (#39257)
## Feature
As the title, support `has` match, `local` that works the same with the `rewrites` and `redirects` of next.config.js on middleware config. With this PR, you can write the config like the following:
```js
export const config = {
matcher: [
"/foo",
{ source: "/bar" },
{
source: "/baz",
has: [
{
type: 'header',
key: 'x-my-header',
value: 'my-value',
}
]
},
{
source: "/en/asdf",
locale: false,
},
]
}
```
Also, fixes https://github.com/vercel/next.js/issues/39428
related https://github.com/vercel/edge-functions/issues/178, https://github.com/vercel/edge-functions/issues/179
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-08-31 18:23:30 +02:00
|
|
|
import type {
|
|
|
|
MiddlewareConfig,
|
|
|
|
MiddlewareMatcher,
|
|
|
|
} from './analysis/get-page-static-info'
|
2022-04-27 11:50:29 +02:00
|
|
|
import type { LoadedEnvFiles } from '@next/env'
|
2021-12-21 16:13:45 +01:00
|
|
|
import chalk from 'next/dist/compiled/chalk'
|
2020-07-09 14:31:06 +02:00
|
|
|
import { posix, join } from 'path'
|
2019-05-29 13:57:26 +02:00
|
|
|
import { stringify } from 'querystring'
|
2022-05-03 12:37:23 +02:00
|
|
|
import {
|
|
|
|
API_ROUTE,
|
|
|
|
DOT_NEXT_ALIAS,
|
|
|
|
PAGES_DIR_ALIAS,
|
2022-05-19 17:46:21 +02:00
|
|
|
ROOT_DIR_ALIAS,
|
2022-05-25 11:46:26 +02:00
|
|
|
APP_DIR_ALIAS,
|
2022-06-27 03:02:24 +02:00
|
|
|
SERVER_RUNTIME,
|
2022-08-12 15:01:19 +02:00
|
|
|
WEBPACK_LAYERS,
|
2022-05-03 12:37:23 +02:00
|
|
|
} from '../lib/constants'
|
2022-09-18 02:00:16 +02:00
|
|
|
import { RSC_MODULE_TYPES } from '../shared/lib/constants'
|
2022-05-03 12:37:23 +02:00
|
|
|
import {
|
|
|
|
CLIENT_STATIC_FILES_RUNTIME_AMP,
|
|
|
|
CLIENT_STATIC_FILES_RUNTIME_MAIN,
|
2022-08-10 21:31:01 +02:00
|
|
|
CLIENT_STATIC_FILES_RUNTIME_MAIN_APP,
|
2022-08-12 15:01:19 +02:00
|
|
|
CLIENT_STATIC_FILES_RUNTIME_POLYFILLS,
|
2022-05-03 12:37:23 +02:00
|
|
|
CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH,
|
2022-08-12 15:01:19 +02:00
|
|
|
CompilerNameValues,
|
|
|
|
COMPILER_NAMES,
|
2022-05-03 12:37:23 +02:00
|
|
|
EDGE_RUNTIME_WEBPACK,
|
|
|
|
} from '../shared/lib/constants'
|
2021-06-30 13:44:40 +02:00
|
|
|
import { __ApiPreviewProps } from '../server/api-utils'
|
2022-01-20 22:25:44 +01:00
|
|
|
import { isTargetLikeServerless } from '../server/utils'
|
2019-09-11 20:26:10 +02:00
|
|
|
import { warn } from './output/log'
|
2022-06-08 16:10:05 +02:00
|
|
|
import {
|
|
|
|
isMiddlewareFile,
|
|
|
|
isMiddlewareFilename,
|
2022-06-08 18:51:26 +02:00
|
|
|
NestedMiddlewareError,
|
2022-06-19 15:17:18 +02:00
|
|
|
MiddlewareInServerlessTargetError,
|
2022-06-08 16:10:05 +02:00
|
|
|
} from './utils'
|
2022-05-20 14:24:00 +02:00
|
|
|
import { getPageStaticInfo } from './analysis/get-page-static-info'
|
2022-04-30 13:19:27 +02:00
|
|
|
import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep'
|
|
|
|
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
|
2022-08-07 21:16:10 +02:00
|
|
|
import { ServerRuntime } from '../types'
|
2022-09-06 19:03:21 +02:00
|
|
|
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
|
feat(next): Support has match and locale option on middleware config (#39257)
## Feature
As the title, support `has` match, `local` that works the same with the `rewrites` and `redirects` of next.config.js on middleware config. With this PR, you can write the config like the following:
```js
export const config = {
matcher: [
"/foo",
{ source: "/bar" },
{
source: "/baz",
has: [
{
type: 'header',
key: 'x-my-header',
value: 'my-value',
}
]
},
{
source: "/en/asdf",
locale: false,
},
]
}
```
Also, fixes https://github.com/vercel/next.js/issues/39428
related https://github.com/vercel/edge-functions/issues/178, https://github.com/vercel/edge-functions/issues/179
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-08-31 18:23:30 +02:00
|
|
|
import { encodeMatchers } from './webpack/loaders/next-middleware-loader'
|
feat(edge): allows configuring Dynamic code execution guard (#39539)
### 📖 What's in there?
Dynamic code evaluation (`eval()`, `new Function()`, ...) is not
supported on the edge runtime, hence why we fail the build when
detecting such statement in the middleware or `experimental-edge` routes
at build time.
However, there could be false positives, which static analysis and
tree-shaking can not exclude:
- `qs` through these dependencies (get-intrinsic:
[source](https://github.com/ljharb/get-intrinsic/blob/main/index.js#L12))
- `function-bind`
([source](https://github.com/Raynos/function-bind/blob/master/implementation.js#L42))
- `has`
([source](https://github.com/tarruda/has/blob/master/src/index.js#L5))
This PR leverages the existing `config` export to let user allow some of
their files.
it’s meant for allowing users to import 3rd party modules who embed
dynamic code evaluation, but do not use it (because or code paths), and
can't be tree-shaked.
By default, it’s keeping the existing behavior: warn in dev, fails to
build.
If users allow dynamic code, and that code is reached at runtime, their
app stills breaks.
### 🧪 How to test?
- (existing) integration tests for disallowing dynamic code evaluation:
`pnpm testheadless --testPathPattern=runtime-dynamic`
- (new) integration tests for allowing dynamic code evaluation: `pnpm
testheadless --testPathPattern=runtime-configurable`
- (amended) production tests for validating the new configuration keys:
`pnpm testheadless --testPathPattern=config-validations`
To try it live, you could have an application such as:
```js
// lib/index.js
/* eslint-disable no-eval */
export function hasUnusedDynamic() {
if ((() => false)()) {
eval('100')
}
}
export function hasDynamic() {
eval('100')
}
// pages/index.jsx
export default function Page({ edgeRoute }) {
return <p>{edgeRoute}</p>
}
export const getServerSideProps = async (req) => {
const res = await fetch(`http://localhost:3000/api/route`)
const data = await res.json()
return { props: { edgeRoute: data.ok ? `Hi from the edge route` : '' } }
}
// pages/api/route.js
import { hasDynamic } from '../../lib'
export default async function handle() {
hasDynamic()
return Response.json({ ok: true })
}
export const config = {
runtime: 'experimental-edge' ,
allowDynamic: '/lib/**'
}
```
Playing with `config.allowDynamic`, you should be able to:
- build the app even if it uses `eval()` (it will obviously fail at
runtime)
- build the app that _imports but does not use_ `eval()`
- run the app in dev, even if it uses `eval()` with no warning
### 🆙 Notes to reviewers
Before adding documentation and telemetry, I'd like to collect comments
on a couple of points:
- the overall design for this feature: is a list of globs useful and
easy enough?
- should the globs be relative to the application root (current
implementation) to to the edge route/middleware file?
- (especially to @sokra) is the implementation idiomatic enough? I've
leverage loaders to read the _entry point_ configuration once, then the
ModuleGraph to get it back during the parsing phase. I couldn't re-use
the existing `getExtractMetadata()` facility since it's happening late
after the parsing.
- there's a glitch with `import { ServerRuntime } from '../../types'` in
`get-page-static-info.ts`
([here](https://github.com/vercel/next.js/pull/39539/files#diff-cb7ac6392c3dd707c5edab159c3144ec114eafea92dad5d98f4eedfc612174d2L12)).
I had to use `next/types` because it was failing during lint. Any clue
why?
### ☑️ Checklist
- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [x] Telemetry added. In case of a feature if it's used or not.
- [x] Errors have helpful link attached, see `contributing.md`
2022-09-13 00:01:00 +02:00
|
|
|
import { EdgeFunctionLoaderOptions } from './webpack/loaders/next-edge-function-loader'
|
2019-01-08 23:10:32 +01:00
|
|
|
|
2021-10-20 19:52:11 +02:00
|
|
|
type ObjectValue<T> = T extends { [key: string]: infer V } ? V : never
|
2019-01-08 23:10:32 +01:00
|
|
|
|
2022-04-27 11:50:29 +02:00
|
|
|
/**
|
2022-05-20 20:07:20 +02:00
|
|
|
* For a given page path removes the provided extensions.
|
2022-04-27 11:50:29 +02:00
|
|
|
*/
|
2022-05-05 13:15:32 +02:00
|
|
|
export function getPageFromPath(pagePath: string, pageExtensions: string[]) {
|
2022-05-03 12:37:23 +02:00
|
|
|
let page = normalizePathSep(
|
2022-05-20 20:07:20 +02:00
|
|
|
pagePath.replace(new RegExp(`\\.+(${pageExtensions.join('|')})$`), '')
|
2022-05-03 12:37:23 +02:00
|
|
|
)
|
|
|
|
|
2022-05-05 13:15:32 +02:00
|
|
|
page = page.replace(/\/index$/, '')
|
2022-04-27 11:50:29 +02:00
|
|
|
|
2022-02-06 02:28:42 +01:00
|
|
|
return page === '' ? '/' : page
|
|
|
|
}
|
|
|
|
|
2022-04-27 11:50:29 +02:00
|
|
|
export function createPagesMapping({
|
|
|
|
isDev,
|
|
|
|
pageExtensions,
|
|
|
|
pagePaths,
|
2022-05-19 17:46:21 +02:00
|
|
|
pagesType,
|
2022-09-03 02:13:47 +02:00
|
|
|
pagesDir,
|
2022-04-27 11:50:29 +02:00
|
|
|
}: {
|
|
|
|
isDev: boolean
|
|
|
|
pageExtensions: string[]
|
|
|
|
pagePaths: string[]
|
2022-05-25 11:46:26 +02:00
|
|
|
pagesType: 'pages' | 'root' | 'app'
|
2022-09-03 02:13:47 +02:00
|
|
|
pagesDir: string | undefined
|
2022-04-27 11:50:29 +02:00
|
|
|
}): { [page: string]: string } {
|
|
|
|
const previousPages: { [key: string]: string } = {}
|
|
|
|
const pages = pagePaths.reduce<{ [key: string]: string }>(
|
|
|
|
(result, pagePath) => {
|
|
|
|
// Do not process .d.ts files inside the `pages` folder
|
|
|
|
if (pagePath.endsWith('.d.ts') && pageExtensions.includes('ts')) {
|
|
|
|
return result
|
|
|
|
}
|
2022-01-01 18:16:03 +01:00
|
|
|
|
2022-05-05 13:15:32 +02:00
|
|
|
const pageKey = getPageFromPath(pagePath, pageExtensions)
|
2022-02-06 02:28:42 +01:00
|
|
|
|
2019-09-11 20:26:10 +02:00
|
|
|
if (pageKey in result) {
|
|
|
|
warn(
|
|
|
|
`Duplicate page detected. ${chalk.cyan(
|
|
|
|
join('pages', previousPages[pageKey])
|
|
|
|
)} and ${chalk.cyan(
|
|
|
|
join('pages', pagePath)
|
|
|
|
)} both resolve to ${chalk.cyan(pageKey)}.`
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
previousPages[pageKey] = pagePath
|
|
|
|
}
|
2022-04-27 11:50:29 +02:00
|
|
|
|
2022-05-19 17:46:21 +02:00
|
|
|
result[pageKey] = normalizePathSep(
|
|
|
|
join(
|
|
|
|
pagesType === 'pages'
|
|
|
|
? PAGES_DIR_ALIAS
|
2022-05-25 11:46:26 +02:00
|
|
|
: pagesType === 'app'
|
|
|
|
? APP_DIR_ALIAS
|
2022-05-19 17:46:21 +02:00
|
|
|
: ROOT_DIR_ALIAS,
|
|
|
|
pagePath
|
|
|
|
)
|
|
|
|
)
|
2019-05-29 13:57:26 +02:00
|
|
|
return result
|
|
|
|
},
|
|
|
|
{}
|
|
|
|
)
|
2019-01-08 23:10:32 +01:00
|
|
|
|
2022-05-19 17:46:21 +02:00
|
|
|
if (pagesType !== 'pages') {
|
2022-05-03 12:37:23 +02:00
|
|
|
return pages
|
|
|
|
}
|
|
|
|
|
2021-10-07 01:46:46 +02:00
|
|
|
if (isDev) {
|
2022-04-27 11:50:29 +02:00
|
|
|
delete pages['/_app']
|
|
|
|
delete pages['/_error']
|
|
|
|
delete pages['/_document']
|
2021-08-19 10:12:12 +02:00
|
|
|
}
|
2019-01-08 23:10:32 +01:00
|
|
|
|
2022-05-19 17:46:21 +02:00
|
|
|
// In development we always alias these to allow Webpack to fallback to
|
|
|
|
// the correct source file so that HMR can work properly when a file is
|
|
|
|
// added or removed.
|
2022-09-03 02:13:47 +02:00
|
|
|
const root = isDev && pagesDir ? PAGES_DIR_ALIAS : 'next/dist/pages'
|
2022-05-19 17:46:21 +02:00
|
|
|
|
2022-04-27 11:50:29 +02:00
|
|
|
return {
|
|
|
|
'/_app': `${root}/_app`,
|
|
|
|
'/_error': `${root}/_error`,
|
|
|
|
'/_document': `${root}/_document`,
|
|
|
|
...pages,
|
|
|
|
}
|
2019-01-08 23:10:32 +01:00
|
|
|
}
|
|
|
|
|
2022-04-27 11:50:29 +02:00
|
|
|
interface CreateEntrypointsParams {
|
|
|
|
buildId: string
|
|
|
|
config: NextConfigComplete
|
|
|
|
envFiles: LoadedEnvFiles
|
2022-04-05 23:51:47 +02:00
|
|
|
isDev?: boolean
|
2022-04-27 11:50:29 +02:00
|
|
|
pages: { [page: string]: string }
|
2022-09-03 02:13:47 +02:00
|
|
|
pagesDir?: string
|
2022-04-27 11:50:29 +02:00
|
|
|
previewMode: __ApiPreviewProps
|
2022-05-19 17:46:21 +02:00
|
|
|
rootDir: string
|
|
|
|
rootPaths?: Record<string, string>
|
2022-04-27 11:50:29 +02:00
|
|
|
target: 'server' | 'serverless' | 'experimental-serverless-trace'
|
2022-05-25 11:46:26 +02:00
|
|
|
appDir?: string
|
|
|
|
appPaths?: Record<string, string>
|
2022-05-07 15:37:14 +02:00
|
|
|
pageExtensions: string[]
|
2022-04-27 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getEdgeServerEntry(opts: {
|
feat(edge): allows configuring Dynamic code execution guard (#39539)
### 📖 What's in there?
Dynamic code evaluation (`eval()`, `new Function()`, ...) is not
supported on the edge runtime, hence why we fail the build when
detecting such statement in the middleware or `experimental-edge` routes
at build time.
However, there could be false positives, which static analysis and
tree-shaking can not exclude:
- `qs` through these dependencies (get-intrinsic:
[source](https://github.com/ljharb/get-intrinsic/blob/main/index.js#L12))
- `function-bind`
([source](https://github.com/Raynos/function-bind/blob/master/implementation.js#L42))
- `has`
([source](https://github.com/tarruda/has/blob/master/src/index.js#L5))
This PR leverages the existing `config` export to let user allow some of
their files.
it’s meant for allowing users to import 3rd party modules who embed
dynamic code evaluation, but do not use it (because or code paths), and
can't be tree-shaked.
By default, it’s keeping the existing behavior: warn in dev, fails to
build.
If users allow dynamic code, and that code is reached at runtime, their
app stills breaks.
### 🧪 How to test?
- (existing) integration tests for disallowing dynamic code evaluation:
`pnpm testheadless --testPathPattern=runtime-dynamic`
- (new) integration tests for allowing dynamic code evaluation: `pnpm
testheadless --testPathPattern=runtime-configurable`
- (amended) production tests for validating the new configuration keys:
`pnpm testheadless --testPathPattern=config-validations`
To try it live, you could have an application such as:
```js
// lib/index.js
/* eslint-disable no-eval */
export function hasUnusedDynamic() {
if ((() => false)()) {
eval('100')
}
}
export function hasDynamic() {
eval('100')
}
// pages/index.jsx
export default function Page({ edgeRoute }) {
return <p>{edgeRoute}</p>
}
export const getServerSideProps = async (req) => {
const res = await fetch(`http://localhost:3000/api/route`)
const data = await res.json()
return { props: { edgeRoute: data.ok ? `Hi from the edge route` : '' } }
}
// pages/api/route.js
import { hasDynamic } from '../../lib'
export default async function handle() {
hasDynamic()
return Response.json({ ok: true })
}
export const config = {
runtime: 'experimental-edge' ,
allowDynamic: '/lib/**'
}
```
Playing with `config.allowDynamic`, you should be able to:
- build the app even if it uses `eval()` (it will obviously fail at
runtime)
- build the app that _imports but does not use_ `eval()`
- run the app in dev, even if it uses `eval()` with no warning
### 🆙 Notes to reviewers
Before adding documentation and telemetry, I'd like to collect comments
on a couple of points:
- the overall design for this feature: is a list of globs useful and
easy enough?
- should the globs be relative to the application root (current
implementation) to to the edge route/middleware file?
- (especially to @sokra) is the implementation idiomatic enough? I've
leverage loaders to read the _entry point_ configuration once, then the
ModuleGraph to get it back during the parsing phase. I couldn't re-use
the existing `getExtractMetadata()` facility since it's happening late
after the parsing.
- there's a glitch with `import { ServerRuntime } from '../../types'` in
`get-page-static-info.ts`
([here](https://github.com/vercel/next.js/pull/39539/files#diff-cb7ac6392c3dd707c5edab159c3144ec114eafea92dad5d98f4eedfc612174d2L12)).
I had to use `next/types` because it was failing during lint. Any clue
why?
### ☑️ Checklist
- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [x] Telemetry added. In case of a feature if it's used or not.
- [x] Errors have helpful link attached, see `contributing.md`
2022-09-13 00:01:00 +02:00
|
|
|
rootDir: string
|
2022-04-27 11:50:29 +02:00
|
|
|
absolutePagePath: string
|
|
|
|
buildId: string
|
|
|
|
bundlePath: string
|
|
|
|
config: NextConfigComplete
|
|
|
|
isDev: boolean
|
2022-05-13 19:48:53 +02:00
|
|
|
isServerComponent: boolean
|
2022-04-27 11:50:29 +02:00
|
|
|
page: string
|
|
|
|
pages: { [page: string]: string }
|
feat(next): Support has match and locale option on middleware config (#39257)
## Feature
As the title, support `has` match, `local` that works the same with the `rewrites` and `redirects` of next.config.js on middleware config. With this PR, you can write the config like the following:
```js
export const config = {
matcher: [
"/foo",
{ source: "/bar" },
{
source: "/baz",
has: [
{
type: 'header',
key: 'x-my-header',
value: 'my-value',
}
]
},
{
source: "/en/asdf",
locale: false,
},
]
}
```
Also, fixes https://github.com/vercel/next.js/issues/39428
related https://github.com/vercel/edge-functions/issues/178, https://github.com/vercel/edge-functions/issues/179
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-08-31 18:23:30 +02:00
|
|
|
middleware?: Partial<MiddlewareConfig>
|
2022-08-24 21:49:47 +02:00
|
|
|
pagesType?: 'app' | 'pages' | 'root'
|
|
|
|
appDirLoader?: string
|
2022-05-13 19:48:53 +02:00
|
|
|
}) {
|
2022-06-08 16:10:05 +02:00
|
|
|
if (isMiddlewareFile(opts.page)) {
|
2022-04-27 11:50:29 +02:00
|
|
|
const loaderParams: MiddlewareLoaderOptions = {
|
|
|
|
absolutePagePath: opts.absolutePagePath,
|
|
|
|
page: opts.page,
|
feat(edge): allows configuring Dynamic code execution guard (#39539)
### 📖 What's in there?
Dynamic code evaluation (`eval()`, `new Function()`, ...) is not
supported on the edge runtime, hence why we fail the build when
detecting such statement in the middleware or `experimental-edge` routes
at build time.
However, there could be false positives, which static analysis and
tree-shaking can not exclude:
- `qs` through these dependencies (get-intrinsic:
[source](https://github.com/ljharb/get-intrinsic/blob/main/index.js#L12))
- `function-bind`
([source](https://github.com/Raynos/function-bind/blob/master/implementation.js#L42))
- `has`
([source](https://github.com/tarruda/has/blob/master/src/index.js#L5))
This PR leverages the existing `config` export to let user allow some of
their files.
it’s meant for allowing users to import 3rd party modules who embed
dynamic code evaluation, but do not use it (because or code paths), and
can't be tree-shaked.
By default, it’s keeping the existing behavior: warn in dev, fails to
build.
If users allow dynamic code, and that code is reached at runtime, their
app stills breaks.
### 🧪 How to test?
- (existing) integration tests for disallowing dynamic code evaluation:
`pnpm testheadless --testPathPattern=runtime-dynamic`
- (new) integration tests for allowing dynamic code evaluation: `pnpm
testheadless --testPathPattern=runtime-configurable`
- (amended) production tests for validating the new configuration keys:
`pnpm testheadless --testPathPattern=config-validations`
To try it live, you could have an application such as:
```js
// lib/index.js
/* eslint-disable no-eval */
export function hasUnusedDynamic() {
if ((() => false)()) {
eval('100')
}
}
export function hasDynamic() {
eval('100')
}
// pages/index.jsx
export default function Page({ edgeRoute }) {
return <p>{edgeRoute}</p>
}
export const getServerSideProps = async (req) => {
const res = await fetch(`http://localhost:3000/api/route`)
const data = await res.json()
return { props: { edgeRoute: data.ok ? `Hi from the edge route` : '' } }
}
// pages/api/route.js
import { hasDynamic } from '../../lib'
export default async function handle() {
hasDynamic()
return Response.json({ ok: true })
}
export const config = {
runtime: 'experimental-edge' ,
allowDynamic: '/lib/**'
}
```
Playing with `config.allowDynamic`, you should be able to:
- build the app even if it uses `eval()` (it will obviously fail at
runtime)
- build the app that _imports but does not use_ `eval()`
- run the app in dev, even if it uses `eval()` with no warning
### 🆙 Notes to reviewers
Before adding documentation and telemetry, I'd like to collect comments
on a couple of points:
- the overall design for this feature: is a list of globs useful and
easy enough?
- should the globs be relative to the application root (current
implementation) to to the edge route/middleware file?
- (especially to @sokra) is the implementation idiomatic enough? I've
leverage loaders to read the _entry point_ configuration once, then the
ModuleGraph to get it back during the parsing phase. I couldn't re-use
the existing `getExtractMetadata()` facility since it's happening late
after the parsing.
- there's a glitch with `import { ServerRuntime } from '../../types'` in
`get-page-static-info.ts`
([here](https://github.com/vercel/next.js/pull/39539/files#diff-cb7ac6392c3dd707c5edab159c3144ec114eafea92dad5d98f4eedfc612174d2L12)).
I had to use `next/types` because it was failing during lint. Any clue
why?
### ☑️ Checklist
- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [x] Telemetry added. In case of a feature if it's used or not.
- [x] Errors have helpful link attached, see `contributing.md`
2022-09-13 00:01:00 +02:00
|
|
|
rootDir: opts.rootDir,
|
feat(next): Support has match and locale option on middleware config (#39257)
## Feature
As the title, support `has` match, `local` that works the same with the `rewrites` and `redirects` of next.config.js on middleware config. With this PR, you can write the config like the following:
```js
export const config = {
matcher: [
"/foo",
{ source: "/bar" },
{
source: "/baz",
has: [
{
type: 'header',
key: 'x-my-header',
value: 'my-value',
}
]
},
{
source: "/en/asdf",
locale: false,
},
]
}
```
Also, fixes https://github.com/vercel/next.js/issues/39428
related https://github.com/vercel/edge-functions/issues/178, https://github.com/vercel/edge-functions/issues/179
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-08-31 18:23:30 +02:00
|
|
|
matchers: opts.middleware?.matchers
|
|
|
|
? encodeMatchers(opts.middleware.matchers)
|
|
|
|
: '',
|
2022-04-27 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return `next-middleware-loader?${stringify(loaderParams)}!`
|
|
|
|
}
|
|
|
|
|
2022-07-20 02:33:00 +02:00
|
|
|
if (opts.page.startsWith('/api/') || opts.page === '/api') {
|
feat(edge): allows configuring Dynamic code execution guard (#39539)
### 📖 What's in there?
Dynamic code evaluation (`eval()`, `new Function()`, ...) is not
supported on the edge runtime, hence why we fail the build when
detecting such statement in the middleware or `experimental-edge` routes
at build time.
However, there could be false positives, which static analysis and
tree-shaking can not exclude:
- `qs` through these dependencies (get-intrinsic:
[source](https://github.com/ljharb/get-intrinsic/blob/main/index.js#L12))
- `function-bind`
([source](https://github.com/Raynos/function-bind/blob/master/implementation.js#L42))
- `has`
([source](https://github.com/tarruda/has/blob/master/src/index.js#L5))
This PR leverages the existing `config` export to let user allow some of
their files.
it’s meant for allowing users to import 3rd party modules who embed
dynamic code evaluation, but do not use it (because or code paths), and
can't be tree-shaked.
By default, it’s keeping the existing behavior: warn in dev, fails to
build.
If users allow dynamic code, and that code is reached at runtime, their
app stills breaks.
### 🧪 How to test?
- (existing) integration tests for disallowing dynamic code evaluation:
`pnpm testheadless --testPathPattern=runtime-dynamic`
- (new) integration tests for allowing dynamic code evaluation: `pnpm
testheadless --testPathPattern=runtime-configurable`
- (amended) production tests for validating the new configuration keys:
`pnpm testheadless --testPathPattern=config-validations`
To try it live, you could have an application such as:
```js
// lib/index.js
/* eslint-disable no-eval */
export function hasUnusedDynamic() {
if ((() => false)()) {
eval('100')
}
}
export function hasDynamic() {
eval('100')
}
// pages/index.jsx
export default function Page({ edgeRoute }) {
return <p>{edgeRoute}</p>
}
export const getServerSideProps = async (req) => {
const res = await fetch(`http://localhost:3000/api/route`)
const data = await res.json()
return { props: { edgeRoute: data.ok ? `Hi from the edge route` : '' } }
}
// pages/api/route.js
import { hasDynamic } from '../../lib'
export default async function handle() {
hasDynamic()
return Response.json({ ok: true })
}
export const config = {
runtime: 'experimental-edge' ,
allowDynamic: '/lib/**'
}
```
Playing with `config.allowDynamic`, you should be able to:
- build the app even if it uses `eval()` (it will obviously fail at
runtime)
- build the app that _imports but does not use_ `eval()`
- run the app in dev, even if it uses `eval()` with no warning
### 🆙 Notes to reviewers
Before adding documentation and telemetry, I'd like to collect comments
on a couple of points:
- the overall design for this feature: is a list of globs useful and
easy enough?
- should the globs be relative to the application root (current
implementation) to to the edge route/middleware file?
- (especially to @sokra) is the implementation idiomatic enough? I've
leverage loaders to read the _entry point_ configuration once, then the
ModuleGraph to get it back during the parsing phase. I couldn't re-use
the existing `getExtractMetadata()` facility since it's happening late
after the parsing.
- there's a glitch with `import { ServerRuntime } from '../../types'` in
`get-page-static-info.ts`
([here](https://github.com/vercel/next.js/pull/39539/files#diff-cb7ac6392c3dd707c5edab159c3144ec114eafea92dad5d98f4eedfc612174d2L12)).
I had to use `next/types` because it was failing during lint. Any clue
why?
### ☑️ Checklist
- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [x] Telemetry added. In case of a feature if it's used or not.
- [x] Errors have helpful link attached, see `contributing.md`
2022-09-13 00:01:00 +02:00
|
|
|
const loaderParams: EdgeFunctionLoaderOptions = {
|
2022-06-13 20:17:44 +02:00
|
|
|
absolutePagePath: opts.absolutePagePath,
|
|
|
|
page: opts.page,
|
feat(edge): allows configuring Dynamic code execution guard (#39539)
### 📖 What's in there?
Dynamic code evaluation (`eval()`, `new Function()`, ...) is not
supported on the edge runtime, hence why we fail the build when
detecting such statement in the middleware or `experimental-edge` routes
at build time.
However, there could be false positives, which static analysis and
tree-shaking can not exclude:
- `qs` through these dependencies (get-intrinsic:
[source](https://github.com/ljharb/get-intrinsic/blob/main/index.js#L12))
- `function-bind`
([source](https://github.com/Raynos/function-bind/blob/master/implementation.js#L42))
- `has`
([source](https://github.com/tarruda/has/blob/master/src/index.js#L5))
This PR leverages the existing `config` export to let user allow some of
their files.
it’s meant for allowing users to import 3rd party modules who embed
dynamic code evaluation, but do not use it (because or code paths), and
can't be tree-shaked.
By default, it’s keeping the existing behavior: warn in dev, fails to
build.
If users allow dynamic code, and that code is reached at runtime, their
app stills breaks.
### 🧪 How to test?
- (existing) integration tests for disallowing dynamic code evaluation:
`pnpm testheadless --testPathPattern=runtime-dynamic`
- (new) integration tests for allowing dynamic code evaluation: `pnpm
testheadless --testPathPattern=runtime-configurable`
- (amended) production tests for validating the new configuration keys:
`pnpm testheadless --testPathPattern=config-validations`
To try it live, you could have an application such as:
```js
// lib/index.js
/* eslint-disable no-eval */
export function hasUnusedDynamic() {
if ((() => false)()) {
eval('100')
}
}
export function hasDynamic() {
eval('100')
}
// pages/index.jsx
export default function Page({ edgeRoute }) {
return <p>{edgeRoute}</p>
}
export const getServerSideProps = async (req) => {
const res = await fetch(`http://localhost:3000/api/route`)
const data = await res.json()
return { props: { edgeRoute: data.ok ? `Hi from the edge route` : '' } }
}
// pages/api/route.js
import { hasDynamic } from '../../lib'
export default async function handle() {
hasDynamic()
return Response.json({ ok: true })
}
export const config = {
runtime: 'experimental-edge' ,
allowDynamic: '/lib/**'
}
```
Playing with `config.allowDynamic`, you should be able to:
- build the app even if it uses `eval()` (it will obviously fail at
runtime)
- build the app that _imports but does not use_ `eval()`
- run the app in dev, even if it uses `eval()` with no warning
### 🆙 Notes to reviewers
Before adding documentation and telemetry, I'd like to collect comments
on a couple of points:
- the overall design for this feature: is a list of globs useful and
easy enough?
- should the globs be relative to the application root (current
implementation) to to the edge route/middleware file?
- (especially to @sokra) is the implementation idiomatic enough? I've
leverage loaders to read the _entry point_ configuration once, then the
ModuleGraph to get it back during the parsing phase. I couldn't re-use
the existing `getExtractMetadata()` facility since it's happening late
after the parsing.
- there's a glitch with `import { ServerRuntime } from '../../types'` in
`get-page-static-info.ts`
([here](https://github.com/vercel/next.js/pull/39539/files#diff-cb7ac6392c3dd707c5edab159c3144ec114eafea92dad5d98f4eedfc612174d2L12)).
I had to use `next/types` because it was failing during lint. Any clue
why?
### ☑️ Checklist
- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [x] Telemetry added. In case of a feature if it's used or not.
- [x] Errors have helpful link attached, see `contributing.md`
2022-09-13 00:01:00 +02:00
|
|
|
rootDir: opts.rootDir,
|
2022-06-13 20:17:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return `next-edge-function-loader?${stringify(loaderParams)}!`
|
|
|
|
}
|
|
|
|
|
2022-06-26 23:01:26 +02:00
|
|
|
const loaderParams: EdgeSSRLoaderQuery = {
|
2022-04-27 11:50:29 +02:00
|
|
|
absolute500Path: opts.pages['/500'] || '',
|
|
|
|
absoluteAppPath: opts.pages['/_app'],
|
|
|
|
absoluteDocumentPath: opts.pages['/_document'],
|
|
|
|
absoluteErrorPath: opts.pages['/_error'],
|
|
|
|
absolutePagePath: opts.absolutePagePath,
|
|
|
|
buildId: opts.buildId,
|
|
|
|
dev: opts.isDev,
|
2022-09-18 02:00:16 +02:00
|
|
|
isServerComponent: opts.isServerComponent,
|
2022-04-27 11:50:29 +02:00
|
|
|
page: opts.page,
|
|
|
|
stringifiedConfig: JSON.stringify(opts.config),
|
2022-08-24 21:49:47 +02:00
|
|
|
pagesType: opts.pagesType,
|
|
|
|
appDirLoader: Buffer.from(opts.appDirLoader || '').toString('base64'),
|
2022-09-09 00:17:15 +02:00
|
|
|
sriEnabled: !opts.isDev && !!opts.config.experimental.sri?.algorithm,
|
2022-04-27 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
2022-05-13 19:48:53 +02:00
|
|
|
return {
|
2022-06-26 23:01:26 +02:00
|
|
|
import: `next-edge-ssr-loader?${stringify(loaderParams)}!`,
|
2022-08-12 15:01:19 +02:00
|
|
|
layer: opts.isServerComponent ? WEBPACK_LAYERS.server : undefined,
|
2022-05-13 19:48:53 +02:00
|
|
|
}
|
2022-04-27 11:50:29 +02:00
|
|
|
}
|
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
export function getAppEntry(opts: {
|
2022-05-13 19:48:53 +02:00
|
|
|
name: string
|
2022-05-07 15:37:14 +02:00
|
|
|
pagePath: string
|
2022-05-25 11:46:26 +02:00
|
|
|
appDir: string
|
2022-09-06 19:03:21 +02:00
|
|
|
appPaths: string[] | null
|
2022-05-07 15:37:14 +02:00
|
|
|
pageExtensions: string[]
|
|
|
|
}) {
|
2022-05-16 11:46:45 +02:00
|
|
|
return {
|
2022-05-25 11:46:26 +02:00
|
|
|
import: `next-app-loader?${stringify(opts)}!`,
|
2022-08-12 15:01:19 +02:00
|
|
|
layer: WEBPACK_LAYERS.server,
|
2022-05-16 11:46:45 +02:00
|
|
|
}
|
2022-05-05 22:42:22 +02:00
|
|
|
}
|
|
|
|
|
2022-04-27 11:50:29 +02:00
|
|
|
export function getServerlessEntry(opts: {
|
|
|
|
absolutePagePath: string
|
|
|
|
buildId: string
|
|
|
|
config: NextConfigComplete
|
|
|
|
envFiles: LoadedEnvFiles
|
|
|
|
page: string
|
|
|
|
previewMode: __ApiPreviewProps
|
|
|
|
pages: { [page: string]: string }
|
2022-08-16 11:55:37 +02:00
|
|
|
}): ObjectValue<webpack.EntryObject> {
|
2022-04-27 11:50:29 +02:00
|
|
|
const loaderParams: ServerlessLoaderQuery = {
|
|
|
|
absolute404Path: opts.pages['/404'] || '',
|
|
|
|
absoluteAppPath: opts.pages['/_app'],
|
|
|
|
absoluteDocumentPath: opts.pages['/_document'],
|
|
|
|
absoluteErrorPath: opts.pages['/_error'],
|
|
|
|
absolutePagePath: opts.absolutePagePath,
|
|
|
|
assetPrefix: opts.config.assetPrefix,
|
|
|
|
basePath: opts.config.basePath,
|
|
|
|
buildId: opts.buildId,
|
|
|
|
canonicalBase: opts.config.amp.canonicalBase || '',
|
2019-01-08 23:10:32 +01:00
|
|
|
distDir: DOT_NEXT_ALIAS,
|
2022-04-27 11:50:29 +02:00
|
|
|
generateEtags: opts.config.generateEtags ? 'true' : '',
|
|
|
|
i18n: opts.config.i18n ? JSON.stringify(opts.config.i18n) : '',
|
2020-06-30 23:33:37 +02:00
|
|
|
// base64 encode to make sure contents don't break webpack URL loading
|
2022-04-27 11:50:29 +02:00
|
|
|
loadedEnvFiles: Buffer.from(JSON.stringify(opts.envFiles)).toString(
|
2020-06-30 23:33:37 +02:00
|
|
|
'base64'
|
|
|
|
),
|
2022-04-27 11:50:29 +02:00
|
|
|
page: opts.page,
|
|
|
|
poweredByHeader: opts.config.poweredByHeader ? 'true' : '',
|
|
|
|
previewProps: JSON.stringify(opts.previewMode),
|
|
|
|
runtimeConfig:
|
|
|
|
Object.keys(opts.config.publicRuntimeConfig).length > 0 ||
|
|
|
|
Object.keys(opts.config.serverRuntimeConfig).length > 0
|
|
|
|
? JSON.stringify({
|
|
|
|
publicRuntimeConfig: opts.config.publicRuntimeConfig,
|
|
|
|
serverRuntimeConfig: opts.config.serverRuntimeConfig,
|
|
|
|
})
|
|
|
|
: '',
|
|
|
|
}
|
|
|
|
|
|
|
|
return `next-serverless-loader?${stringify(loaderParams)}!`
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getClientEntry(opts: {
|
|
|
|
absolutePagePath: string
|
|
|
|
page: string
|
|
|
|
}) {
|
|
|
|
const loaderOptions: ClientPagesLoaderOptions = {
|
|
|
|
absolutePagePath: opts.absolutePagePath,
|
|
|
|
page: opts.page,
|
2019-01-08 23:10:32 +01:00
|
|
|
}
|
|
|
|
|
2022-04-27 11:50:29 +02:00
|
|
|
const pageLoader = `next-client-pages-loader?${stringify(loaderOptions)}!`
|
|
|
|
|
|
|
|
// Make sure next/router is a dependency of _app or else chunk splitting
|
|
|
|
// might cause the router to not be able to load causing hydration
|
|
|
|
// to fail
|
|
|
|
return opts.page === '/_app'
|
|
|
|
? [pageLoader, require.resolve('../client/router')]
|
|
|
|
: pageLoader
|
|
|
|
}
|
|
|
|
|
2022-08-15 16:29:51 +02:00
|
|
|
export async function runDependingOnPageType<T>(params: {
|
|
|
|
onClient: () => T
|
|
|
|
onEdgeServer: () => T
|
|
|
|
onServer: () => T
|
|
|
|
page: string
|
|
|
|
pageRuntime: ServerRuntime
|
|
|
|
}): Promise<void> {
|
|
|
|
if (isMiddlewareFile(params.page)) {
|
|
|
|
await params.onEdgeServer()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (params.page.match(API_ROUTE)) {
|
|
|
|
if (params.pageRuntime === SERVER_RUNTIME.edge) {
|
|
|
|
await params.onEdgeServer()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
await params.onServer()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (params.page === '/_document') {
|
|
|
|
await params.onServer()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
params.page === '/_app' ||
|
|
|
|
params.page === '/_error' ||
|
|
|
|
params.page === '/404' ||
|
|
|
|
params.page === '/500'
|
|
|
|
) {
|
|
|
|
await Promise.all([params.onClient(), params.onServer()])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (params.pageRuntime === SERVER_RUNTIME.edge) {
|
|
|
|
await Promise.all([params.onClient(), params.onEdgeServer()])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all([params.onClient(), params.onServer()])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-04-27 11:50:29 +02:00
|
|
|
export async function createEntrypoints(params: CreateEntrypointsParams) {
|
2022-05-07 15:37:14 +02:00
|
|
|
const {
|
|
|
|
config,
|
|
|
|
pages,
|
|
|
|
pagesDir,
|
|
|
|
isDev,
|
2022-05-19 17:46:21 +02:00
|
|
|
rootDir,
|
|
|
|
rootPaths,
|
2022-05-07 15:37:14 +02:00
|
|
|
target,
|
2022-05-25 11:46:26 +02:00
|
|
|
appDir,
|
|
|
|
appPaths,
|
2022-05-07 15:37:14 +02:00
|
|
|
pageExtensions,
|
|
|
|
} = params
|
2022-08-16 11:55:37 +02:00
|
|
|
const edgeServer: webpack.EntryObject = {}
|
|
|
|
const server: webpack.EntryObject = {}
|
|
|
|
const client: webpack.EntryObject = {}
|
2022-06-08 18:51:26 +02:00
|
|
|
const nestedMiddleware: string[] = []
|
feat(next): Support has match and locale option on middleware config (#39257)
## Feature
As the title, support `has` match, `local` that works the same with the `rewrites` and `redirects` of next.config.js on middleware config. With this PR, you can write the config like the following:
```js
export const config = {
matcher: [
"/foo",
{ source: "/bar" },
{
source: "/baz",
has: [
{
type: 'header',
key: 'x-my-header',
value: 'my-value',
}
]
},
{
source: "/en/asdf",
locale: false,
},
]
}
```
Also, fixes https://github.com/vercel/next.js/issues/39428
related https://github.com/vercel/edge-functions/issues/178, https://github.com/vercel/edge-functions/issues/179
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-08-31 18:23:30 +02:00
|
|
|
let middlewareMatchers: MiddlewareMatcher[] | undefined = undefined
|
2022-04-27 11:50:29 +02:00
|
|
|
|
2022-09-06 19:03:21 +02:00
|
|
|
let appPathsPerRoute: Record<string, string[]> = {}
|
|
|
|
if (appDir && appPaths) {
|
|
|
|
for (const pathname in appPaths) {
|
|
|
|
const normalizedPath = normalizeAppPath(pathname) || '/'
|
|
|
|
if (!appPathsPerRoute[normalizedPath]) {
|
|
|
|
appPathsPerRoute[normalizedPath] = []
|
|
|
|
}
|
|
|
|
appPathsPerRoute[normalizedPath].push(pathname)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure to sort parallel routes to make the result deterministic.
|
|
|
|
appPathsPerRoute = Object.fromEntries(
|
|
|
|
Object.entries(appPathsPerRoute).map(([k, v]) => [k, v.sort()])
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-05-03 12:37:23 +02:00
|
|
|
const getEntryHandler =
|
2022-05-25 11:46:26 +02:00
|
|
|
(mappings: Record<string, string>, pagesType: 'app' | 'pages' | 'root') =>
|
2022-05-03 12:37:23 +02:00
|
|
|
async (page: string) => {
|
2022-03-08 21:55:14 +01:00
|
|
|
const bundleFile = normalizePagePath(page)
|
2022-07-08 12:08:37 +02:00
|
|
|
const clientBundlePath = posix.join(pagesType, bundleFile)
|
2022-05-19 17:46:21 +02:00
|
|
|
const serverBundlePath =
|
|
|
|
pagesType === 'pages'
|
|
|
|
? posix.join('pages', bundleFile)
|
2022-05-25 11:46:26 +02:00
|
|
|
: pagesType === 'app'
|
|
|
|
? posix.join('app', bundleFile)
|
2022-05-19 17:46:21 +02:00
|
|
|
: bundleFile.slice(1)
|
2022-05-13 19:48:53 +02:00
|
|
|
const absolutePagePath = mappings[page]
|
2022-05-03 12:37:23 +02:00
|
|
|
|
|
|
|
// Handle paths that have aliases
|
|
|
|
const pageFilePath = (() => {
|
2022-09-03 02:13:47 +02:00
|
|
|
if (absolutePagePath.startsWith(PAGES_DIR_ALIAS) && pagesDir) {
|
2022-05-03 12:37:23 +02:00
|
|
|
return absolutePagePath.replace(PAGES_DIR_ALIAS, pagesDir)
|
|
|
|
}
|
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
if (absolutePagePath.startsWith(APP_DIR_ALIAS) && appDir) {
|
|
|
|
return absolutePagePath.replace(APP_DIR_ALIAS, appDir)
|
2022-05-03 12:37:23 +02:00
|
|
|
}
|
|
|
|
|
2022-05-19 17:46:21 +02:00
|
|
|
if (absolutePagePath.startsWith(ROOT_DIR_ALIAS)) {
|
|
|
|
return absolutePagePath.replace(ROOT_DIR_ALIAS, rootDir)
|
|
|
|
}
|
|
|
|
|
2022-05-03 12:37:23 +02:00
|
|
|
return require.resolve(absolutePagePath)
|
|
|
|
})()
|
2019-05-11 15:32:38 +02:00
|
|
|
|
2022-05-19 17:46:21 +02:00
|
|
|
/**
|
|
|
|
* When we find a middleware file that is not in the ROOT_DIR we fail.
|
|
|
|
* There is no need to check on `dev` as this should only happen when
|
|
|
|
* building for production.
|
|
|
|
*/
|
|
|
|
if (
|
|
|
|
!absolutePagePath.startsWith(ROOT_DIR_ALIAS) &&
|
|
|
|
/[\\\\/]_middleware$/.test(page)
|
|
|
|
) {
|
2022-06-08 18:51:26 +02:00
|
|
|
nestedMiddleware.push(page)
|
2022-05-19 17:46:21 +02:00
|
|
|
}
|
|
|
|
|
2022-09-18 02:00:16 +02:00
|
|
|
const isInsideAppDir =
|
|
|
|
!!appDir &&
|
|
|
|
(absolutePagePath.startsWith(APP_DIR_ALIAS) ||
|
|
|
|
absolutePagePath.startsWith(appDir))
|
2022-05-13 19:48:53 +02:00
|
|
|
|
2022-05-20 14:24:00 +02:00
|
|
|
const staticInfo = await getPageStaticInfo({
|
|
|
|
nextConfig: config,
|
|
|
|
pageFilePath,
|
|
|
|
isDev,
|
2022-06-03 18:35:44 +02:00
|
|
|
page,
|
2022-05-20 14:24:00 +02:00
|
|
|
})
|
|
|
|
|
2022-09-18 02:00:16 +02:00
|
|
|
const isServerComponent =
|
|
|
|
isInsideAppDir && staticInfo.rsc !== RSC_MODULE_TYPES.client
|
|
|
|
|
2022-06-14 18:50:05 +02:00
|
|
|
if (isMiddlewareFile(page)) {
|
feat(next): Support has match and locale option on middleware config (#39257)
## Feature
As the title, support `has` match, `local` that works the same with the `rewrites` and `redirects` of next.config.js on middleware config. With this PR, you can write the config like the following:
```js
export const config = {
matcher: [
"/foo",
{ source: "/bar" },
{
source: "/baz",
has: [
{
type: 'header',
key: 'x-my-header',
value: 'my-value',
}
]
},
{
source: "/en/asdf",
locale: false,
},
]
}
```
Also, fixes https://github.com/vercel/next.js/issues/39428
related https://github.com/vercel/edge-functions/issues/178, https://github.com/vercel/edge-functions/issues/179
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-08-31 18:23:30 +02:00
|
|
|
middlewareMatchers = staticInfo.middleware?.matchers ?? [
|
|
|
|
{ regexp: '.*' },
|
|
|
|
]
|
2022-06-19 15:17:18 +02:00
|
|
|
|
|
|
|
if (target === 'serverless') {
|
|
|
|
throw new MiddlewareInServerlessTargetError()
|
|
|
|
}
|
2022-06-14 18:50:05 +02:00
|
|
|
}
|
|
|
|
|
2022-08-12 15:01:19 +02:00
|
|
|
await runDependingOnPageType({
|
2022-04-27 11:50:29 +02:00
|
|
|
page,
|
2022-05-20 14:24:00 +02:00
|
|
|
pageRuntime: staticInfo.runtime,
|
2022-04-27 11:50:29 +02:00
|
|
|
onClient: () => {
|
2022-06-09 16:58:10 +02:00
|
|
|
if (isServerComponent || isInsideAppDir) {
|
2022-05-13 19:48:53 +02:00
|
|
|
// We skip the initial entries for server component pages and let the
|
|
|
|
// server compiler inject them instead.
|
|
|
|
} else {
|
|
|
|
client[clientBundlePath] = getClientEntry({
|
|
|
|
absolutePagePath: mappings[page],
|
|
|
|
page,
|
|
|
|
})
|
|
|
|
}
|
2022-04-27 11:50:29 +02:00
|
|
|
},
|
|
|
|
onServer: () => {
|
2022-05-25 11:46:26 +02:00
|
|
|
if (pagesType === 'app' && appDir) {
|
2022-09-06 19:03:21 +02:00
|
|
|
const matchedAppPaths =
|
|
|
|
appPathsPerRoute[normalizeAppPath(page) || '/']
|
2022-05-25 11:46:26 +02:00
|
|
|
server[serverBundlePath] = getAppEntry({
|
2022-05-13 19:48:53 +02:00
|
|
|
name: serverBundlePath,
|
2022-05-05 22:42:22 +02:00
|
|
|
pagePath: mappings[page],
|
2022-05-25 11:46:26 +02:00
|
|
|
appDir,
|
2022-09-06 19:03:21 +02:00
|
|
|
appPaths: matchedAppPaths,
|
2022-05-07 15:37:14 +02:00
|
|
|
pageExtensions,
|
2022-05-05 22:42:22 +02:00
|
|
|
})
|
|
|
|
} else if (isTargetLikeServerless(target)) {
|
2022-04-27 11:50:29 +02:00
|
|
|
if (page !== '/_app' && page !== '/_document') {
|
|
|
|
server[serverBundlePath] = getServerlessEntry({
|
|
|
|
...params,
|
2022-05-03 12:37:23 +02:00
|
|
|
absolutePagePath: mappings[page],
|
2022-04-27 11:50:29 +02:00
|
|
|
page,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
2022-09-03 02:13:47 +02:00
|
|
|
server[serverBundlePath] = [mappings[page]]
|
2022-04-27 11:50:29 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
onEdgeServer: () => {
|
2022-09-06 19:03:21 +02:00
|
|
|
let appDirLoader: string = ''
|
|
|
|
if (pagesType === 'app') {
|
|
|
|
const matchedAppPaths =
|
|
|
|
appPathsPerRoute[normalizeAppPath(page) || '/']
|
|
|
|
appDirLoader = getAppEntry({
|
|
|
|
name: serverBundlePath,
|
|
|
|
pagePath: mappings[page],
|
|
|
|
appDir: appDir!,
|
|
|
|
appPaths: matchedAppPaths,
|
|
|
|
pageExtensions,
|
|
|
|
}).import
|
|
|
|
}
|
2022-08-24 21:49:47 +02:00
|
|
|
|
2022-04-27 11:50:29 +02:00
|
|
|
edgeServer[serverBundlePath] = getEdgeServerEntry({
|
|
|
|
...params,
|
feat(edge): allows configuring Dynamic code execution guard (#39539)
### 📖 What's in there?
Dynamic code evaluation (`eval()`, `new Function()`, ...) is not
supported on the edge runtime, hence why we fail the build when
detecting such statement in the middleware or `experimental-edge` routes
at build time.
However, there could be false positives, which static analysis and
tree-shaking can not exclude:
- `qs` through these dependencies (get-intrinsic:
[source](https://github.com/ljharb/get-intrinsic/blob/main/index.js#L12))
- `function-bind`
([source](https://github.com/Raynos/function-bind/blob/master/implementation.js#L42))
- `has`
([source](https://github.com/tarruda/has/blob/master/src/index.js#L5))
This PR leverages the existing `config` export to let user allow some of
their files.
it’s meant for allowing users to import 3rd party modules who embed
dynamic code evaluation, but do not use it (because or code paths), and
can't be tree-shaked.
By default, it’s keeping the existing behavior: warn in dev, fails to
build.
If users allow dynamic code, and that code is reached at runtime, their
app stills breaks.
### 🧪 How to test?
- (existing) integration tests for disallowing dynamic code evaluation:
`pnpm testheadless --testPathPattern=runtime-dynamic`
- (new) integration tests for allowing dynamic code evaluation: `pnpm
testheadless --testPathPattern=runtime-configurable`
- (amended) production tests for validating the new configuration keys:
`pnpm testheadless --testPathPattern=config-validations`
To try it live, you could have an application such as:
```js
// lib/index.js
/* eslint-disable no-eval */
export function hasUnusedDynamic() {
if ((() => false)()) {
eval('100')
}
}
export function hasDynamic() {
eval('100')
}
// pages/index.jsx
export default function Page({ edgeRoute }) {
return <p>{edgeRoute}</p>
}
export const getServerSideProps = async (req) => {
const res = await fetch(`http://localhost:3000/api/route`)
const data = await res.json()
return { props: { edgeRoute: data.ok ? `Hi from the edge route` : '' } }
}
// pages/api/route.js
import { hasDynamic } from '../../lib'
export default async function handle() {
hasDynamic()
return Response.json({ ok: true })
}
export const config = {
runtime: 'experimental-edge' ,
allowDynamic: '/lib/**'
}
```
Playing with `config.allowDynamic`, you should be able to:
- build the app even if it uses `eval()` (it will obviously fail at
runtime)
- build the app that _imports but does not use_ `eval()`
- run the app in dev, even if it uses `eval()` with no warning
### 🆙 Notes to reviewers
Before adding documentation and telemetry, I'd like to collect comments
on a couple of points:
- the overall design for this feature: is a list of globs useful and
easy enough?
- should the globs be relative to the application root (current
implementation) to to the edge route/middleware file?
- (especially to @sokra) is the implementation idiomatic enough? I've
leverage loaders to read the _entry point_ configuration once, then the
ModuleGraph to get it back during the parsing phase. I couldn't re-use
the existing `getExtractMetadata()` facility since it's happening late
after the parsing.
- there's a glitch with `import { ServerRuntime } from '../../types'` in
`get-page-static-info.ts`
([here](https://github.com/vercel/next.js/pull/39539/files#diff-cb7ac6392c3dd707c5edab159c3144ec114eafea92dad5d98f4eedfc612174d2L12)).
I had to use `next/types` because it was failing during lint. Any clue
why?
### ☑️ Checklist
- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [x] Telemetry added. In case of a feature if it's used or not.
- [x] Errors have helpful link attached, see `contributing.md`
2022-09-13 00:01:00 +02:00
|
|
|
rootDir,
|
2022-05-03 12:37:23 +02:00
|
|
|
absolutePagePath: mappings[page],
|
2022-04-27 11:50:29 +02:00
|
|
|
bundlePath: clientBundlePath,
|
|
|
|
isDev: false,
|
2022-05-13 19:48:53 +02:00
|
|
|
isServerComponent,
|
2022-04-27 11:50:29 +02:00
|
|
|
page,
|
2022-06-03 18:35:44 +02:00
|
|
|
middleware: staticInfo?.middleware,
|
2022-08-24 21:49:47 +02:00
|
|
|
pagesType,
|
|
|
|
appDirLoader,
|
2022-04-27 11:50:29 +02:00
|
|
|
})
|
|
|
|
},
|
|
|
|
})
|
2022-05-03 12:37:23 +02:00
|
|
|
}
|
|
|
|
|
2022-05-25 11:46:26 +02:00
|
|
|
if (appDir && appPaths) {
|
|
|
|
const entryHandler = getEntryHandler(appPaths, 'app')
|
|
|
|
await Promise.all(Object.keys(appPaths).map(entryHandler))
|
2022-05-03 12:37:23 +02:00
|
|
|
}
|
2022-05-19 17:46:21 +02:00
|
|
|
if (rootPaths) {
|
|
|
|
await Promise.all(
|
|
|
|
Object.keys(rootPaths).map(getEntryHandler(rootPaths, 'root'))
|
|
|
|
)
|
|
|
|
}
|
|
|
|
await Promise.all(Object.keys(pages).map(getEntryHandler(pages, 'pages')))
|
2019-01-08 23:10:32 +01:00
|
|
|
|
2022-06-08 18:51:26 +02:00
|
|
|
if (nestedMiddleware.length > 0) {
|
2022-09-03 02:13:47 +02:00
|
|
|
throw new NestedMiddlewareError(nestedMiddleware, rootDir, pagesDir!)
|
2022-06-08 18:51:26 +02:00
|
|
|
}
|
|
|
|
|
2019-01-08 23:10:32 +01:00
|
|
|
return {
|
|
|
|
client,
|
2019-05-29 13:57:26 +02:00
|
|
|
server,
|
2022-02-08 14:16:46 +01:00
|
|
|
edgeServer,
|
feat(next): Support has match and locale option on middleware config (#39257)
## Feature
As the title, support `has` match, `local` that works the same with the `rewrites` and `redirects` of next.config.js on middleware config. With this PR, you can write the config like the following:
```js
export const config = {
matcher: [
"/foo",
{ source: "/bar" },
{
source: "/baz",
has: [
{
type: 'header',
key: 'x-my-header',
value: 'my-value',
}
]
},
{
source: "/en/asdf",
locale: false,
},
]
}
```
Also, fixes https://github.com/vercel/next.js/issues/39428
related https://github.com/vercel/edge-functions/issues/178, https://github.com/vercel/edge-functions/issues/179
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-08-31 18:23:30 +02:00
|
|
|
middlewareMatchers,
|
2019-01-08 23:10:32 +01:00
|
|
|
}
|
|
|
|
}
|
2021-09-17 21:20:09 +02:00
|
|
|
|
2021-10-20 19:52:11 +02:00
|
|
|
export function finalizeEntrypoint({
|
|
|
|
name,
|
2022-04-27 11:50:29 +02:00
|
|
|
compilerType,
|
2021-10-20 19:52:11 +02:00
|
|
|
value,
|
2022-05-13 19:48:53 +02:00
|
|
|
isServerComponent,
|
2022-06-01 13:52:57 +02:00
|
|
|
appDir,
|
2021-10-20 19:52:11 +02:00
|
|
|
}: {
|
2022-08-12 15:01:19 +02:00
|
|
|
compilerType?: CompilerNameValues
|
2021-10-20 19:52:11 +02:00
|
|
|
name: string
|
2022-08-16 11:55:37 +02:00
|
|
|
value: ObjectValue<webpack.EntryObject>
|
2022-05-13 19:48:53 +02:00
|
|
|
isServerComponent?: boolean
|
2022-06-01 13:52:57 +02:00
|
|
|
appDir?: boolean
|
2022-08-16 11:55:37 +02:00
|
|
|
}): ObjectValue<webpack.EntryObject> {
|
2021-10-20 19:52:11 +02:00
|
|
|
const entry =
|
|
|
|
typeof value !== 'object' || Array.isArray(value)
|
|
|
|
? { import: value }
|
|
|
|
: value
|
|
|
|
|
feat: build edge functions with node.js modules and fail at runtime (#38234)
## What's in there?
The Edge runtime [does not support Node.js modules](https://edge-runtime.vercel.app/features/available-apis#unsupported-apis).
When building Next.js application, we currently fail the build when detecting node.js module imported from middleware.
This is an blocker for using code that is conditionally loading node.js modules (based on platform/env detection), as @cramforce reported.
This PR implements a new strategy where:
- we can build such middleware/Edge API route code **with a warning**
- we fail at run time, with graceful errors in dev (console & react-dev-overlay error)
- we fail at run time, with console errors in production
## How to test?
All cases are covered with integration tests.
To try them live, create a simple app with a page, a `middleware.js` file and a `pages/api/route.js`file.
Here are iconic examples:
### node.js modules
```js
// middleware.js
import { NextResponse } from 'next/server'
// static
import { basename } from 'path'
export default async function middleware() {
// dynamic
const { basename } = await import('path')
basename()
return NextResponse.next()
}
export const config = { matcher: '/' }
```
```js
// pags/api/route.js
// static
import { isAbsolute } from 'path'
export default async function handle() {
// dynamic
const { isAbsolute } = await import('path')
return Response.json({ useNodeModule: isAbsolute('/test') })
}
export const config = { runtime: 'experimental-edge' }
```
Desired error (+ source code highlight in dev):
> The edge runtime does not support Node.js 'path' module
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
Desired warning at build time:
> A Node.js module is loaded ('path' at line 2) which is not supported in the Edge Runtime.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
- [x] in dev middleware, static, shows desired error on stderr
- [x] in dev route, static, shows desired error on stderr
- [x] in dev middleware, dynamic, shows desired error on stderr
- [x] in dev route, dynamic, shows desired error on stderr
- [x] in dev middleware, static, shows desired error on react error overlay
- [x] in dev route, static, shows desired error on react error overlay
- [x] in dev middleware, dynamic, shows desired error on react error overlay
- [x] in dev route, dynamic, shows desired error on react error overlay
- [x] builds middleware successfully, shows build warning, shows desired error on stderr on call
- [x] builds route successfully, shows build warning, shows desired error on stderr on call
### 3rd party modules not found
```js
// middleware.js
import { NextResponse } from 'next/server'
// static
import Unknown from 'unknown'
export default async function middleware() {
// dynamic
const Unknown = await import('unknown')
new Unknown()
return NextResponse.next()
}
```
export const config = { matcher: '/' }
```
```js
// pags/api/route.js
// static
import Unknown from 'unknown'
export default async function handle() {
// dynamic
const Unknown = await import('unknown')
return Response.json({ use3rdPartyModule: Unknown() })
}
export const config = { runtime: 'experimental-edge' }
```
Desired error (+ source code highlight in dev):
> Module not found: Can't resolve 'does-not-exist'
Learn More: https://nextjs.org/docs/messages/module-not-found
- [x] in dev middleware, static, shows desired error on stderr
- [x] in dev route, static, shows desired error on stderr
- [x] in dev middleware, dynamic, shows desired error on stderr
- [x] in dev route, dynamic, shows desired error on stderr
- [x] in dev middleware, static, shows desired error on react error overlay
- [x] in dev route, static, shows desired error on react error overlay
- [x] in dev middleware, dynamic, shows desired error on react error overlay
- [x] in dev route, dynamic, shows desired error on react error overlay
- [x] fails to build middleware, with desired error on stderr
- [x] fails to build route, with desired error on stderr
### unused node.js modules
```js
// middleware.js
import { NextResponse } from 'next/server'
export default async function middleware() {
if (process.exit) {
const { basename } = await import('path')
basename()
}
return NextResponse.next()
}
```
```js
// pags/api/route.js
export default async function handle() {
if (process.exit) {
const { basename } = await import('path')
basename()
}
return Response.json({ useNodeModule: false })
}
export const config = { runtime: 'experimental-edge' }
```
Desired warning at build time:
> A Node.js module is loaded ('path' at line 2) which is not supported in the Edge Runtime.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
- [x] invoke middleware in dev with no error
- [x] invoke route in dev with no error
- [x] builds successfully, shows build warning, invoke middleware with no error
- [x] builds successfully, shows build warning, invoke api-route with no error
## Notes to reviewers
The strategy to implement this feature is to leverages webpack [externals](https://webpack.js.org/configuration/externals/#externals) and run a global `__unsupported_module()` function when using a node.js module from edge function's code.
For the record, I tried using [webpack resolve.fallback](https://webpack.js.org/configuration/resolve/#resolvefallback) and [Webpack.IgnorePlugin](https://webpack.js.org/plugins/ignore-plugin/) but they do not allow throwing proper errors at runtime that would contain the loaded module name for reporting.
`__unsupported_module()` is defined in `EdgeRuntime`, and returns a proxy that's throw on use (whether it's property access, function call, new operator... synchronous & promise-based styles).
However there's an issue with error reporting: webpack does not includes the import lines in the generated sourcemaps, preventing from displaying useful errors.
I extended our middleware-plugin to supplement the sourcemaps (when analyzing edge function code, it saves which module is imported from which file, together with line/column/source)
The react-dev-overlay was adapted to look for this additional information when the caught error relates to modules, instead of looking at sourcemaps.
I removed the previous mechanism (built by @nkzawa ) which caught webpack errors at built time to change the displayed error message (files `next/build/index.js`, `next/build/utils.ts` and `wellknown-errors-plugin`)
2022-07-06 22:54:44 +02:00
|
|
|
const isApi = name.startsWith('pages/api/')
|
2022-08-12 15:01:19 +02:00
|
|
|
if (compilerType === COMPILER_NAMES.server) {
|
2021-10-20 19:52:11 +02:00
|
|
|
return {
|
|
|
|
publicPath: isApi ? '' : undefined,
|
|
|
|
runtime: isApi ? 'webpack-api-runtime' : 'webpack-runtime',
|
2022-08-12 15:01:19 +02:00
|
|
|
layer: isApi
|
|
|
|
? WEBPACK_LAYERS.api
|
|
|
|
: isServerComponent
|
|
|
|
? WEBPACK_LAYERS.server
|
|
|
|
: undefined,
|
2021-10-20 19:52:11 +02:00
|
|
|
...entry,
|
2021-10-06 17:40:01 +02:00
|
|
|
}
|
2021-10-20 19:52:11 +02:00
|
|
|
}
|
|
|
|
|
2022-08-12 15:01:19 +02:00
|
|
|
if (compilerType === COMPILER_NAMES.edgeServer) {
|
2022-04-27 11:50:29 +02:00
|
|
|
return {
|
2022-08-12 15:01:19 +02:00
|
|
|
layer:
|
|
|
|
isMiddlewareFilename(name) || isApi
|
|
|
|
? WEBPACK_LAYERS.middleware
|
|
|
|
: undefined,
|
2022-04-27 11:50:29 +02:00
|
|
|
library: { name: ['_ENTRIES', `middleware_[name]`], type: 'assign' },
|
|
|
|
runtime: EDGE_RUNTIME_WEBPACK,
|
2022-01-10 18:41:53 +01:00
|
|
|
asyncChunks: false,
|
2021-10-20 19:52:11 +02:00
|
|
|
...entry,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
2022-04-27 11:50:29 +02:00
|
|
|
// Client special cases
|
2022-08-12 15:01:19 +02:00
|
|
|
name !== CLIENT_STATIC_FILES_RUNTIME_POLYFILLS &&
|
2022-05-03 12:37:23 +02:00
|
|
|
name !== CLIENT_STATIC_FILES_RUNTIME_MAIN &&
|
2022-08-10 21:31:01 +02:00
|
|
|
name !== CLIENT_STATIC_FILES_RUNTIME_MAIN_APP &&
|
2022-05-03 12:37:23 +02:00
|
|
|
name !== CLIENT_STATIC_FILES_RUNTIME_AMP &&
|
|
|
|
name !== CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH
|
2021-10-20 19:52:11 +02:00
|
|
|
) {
|
2022-07-12 18:32:27 +02:00
|
|
|
// TODO-APP: this is a temporary fix. @shuding is going to change the handling of server components
|
2022-06-01 13:52:57 +02:00
|
|
|
if (appDir && entry.import.includes('flight')) {
|
|
|
|
return {
|
2022-08-10 21:31:01 +02:00
|
|
|
dependOn: CLIENT_STATIC_FILES_RUNTIME_MAIN_APP,
|
2022-06-01 13:52:57 +02:00
|
|
|
...entry,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-20 19:52:11 +02:00
|
|
|
return {
|
|
|
|
dependOn:
|
2021-10-06 17:40:01 +02:00
|
|
|
name.startsWith('pages/') && name !== 'pages/_app'
|
|
|
|
? 'pages/_app'
|
2022-06-01 13:52:57 +02:00
|
|
|
: CLIENT_STATIC_FILES_RUNTIME_MAIN,
|
2021-10-20 19:52:11 +02:00
|
|
|
...entry,
|
2021-09-17 21:20:09 +02:00
|
|
|
}
|
|
|
|
}
|
2021-10-20 19:52:11 +02:00
|
|
|
|
|
|
|
return entry
|
2021-09-17 21:20:09 +02:00
|
|
|
}
|