Skip to content

Commit

Permalink
Merge branch 'canary' into fix/minimal-400-handling
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] committed Nov 5, 2021
2 parents ce1dd47 + b75b2f0 commit 659f3b0
Show file tree
Hide file tree
Showing 50 changed files with 378 additions and 69 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Expand Up @@ -17,5 +17,5 @@
"registry": "https://registry.npmjs.org/"
}
},
"version": "12.0.3-canary.10"
"version": "12.0.3"
}
2 changes: 1 addition & 1 deletion packages/create-next-app/package.json
@@ -1,6 +1,6 @@
{
"name": "create-next-app",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"keywords": [
"react",
"next",
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-config-next/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-config-next",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"description": "ESLint configuration used by NextJS.",
"main": "index.js",
"license": "MIT",
Expand All @@ -9,7 +9,7 @@
"directory": "packages/eslint-config-next"
},
"dependencies": {
"@next/eslint-plugin-next": "12.0.3-canary.10",
"@next/eslint-plugin-next": "12.0.3",
"@rushstack/eslint-patch": "^1.0.6",
"@typescript-eslint/parser": "^4.20.0",
"eslint-import-resolver-node": "^0.3.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-next/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/eslint-plugin-next",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"description": "ESLint plugin for NextJS.",
"main": "lib/index.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-bundle-analyzer/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/bundle-analyzer",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-codemod/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/codemod",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"license": "MIT",
"dependencies": {
"chalk": "4.1.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-env/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/env",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"keywords": [
"react",
"next",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-mdx/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/mdx",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-plugin-storybook/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/plugin-storybook",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-storybook"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-module/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-module",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"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",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-nomodule/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-nomodule",
"version": "12.0.3-canary.10",
"version": "12.0.3",
"description": "A polyfill for non-dead, nomodule browsers.",
"main": "dist/polyfill-nomodule.js",
"license": "MIT",
Expand Down
8 changes: 8 additions & 0 deletions packages/next/build/webpack-config.ts
Expand Up @@ -676,6 +676,14 @@ export default async function getBaseWebpackConfig(
false,
}
: {}),

...(webServerRuntime
? {
'react-dom/server': dev
? 'react-dom/cjs/react-dom-server.browser.development'
: 'react-dom/cjs/react-dom-server.browser.production.min',
}
: {}),
},
...(targetWeb
? {
Expand Down
Expand Up @@ -57,10 +57,6 @@ export default async function middlewareRSCLoader(this: any) {
throw new Error('Your page must export a \`default\` component')
}
function renderError(err, status) {
return new Response(err.toString(), {status})
}
function wrapReadable(readable) {
const encoder = new TextEncoder()
const transformStream = new TransformStream()
Expand Down Expand Up @@ -104,15 +100,10 @@ export default async function middlewareRSCLoader(this: any) {
const Component = Page`
}
function render(request) {
async function render(request) {
const url = request.nextUrl
const query = Object.fromEntries(url.searchParams)
if (Document.getInitialProps) {
const err = new Error('Document.getInitialProps is not supported with server components, please remove it from pages/_document')
return renderError(err, 500)
}
// Preflight request
if (request.method === 'HEAD') {
return new Response('OK.', {
Expand Down Expand Up @@ -172,18 +163,27 @@ export default async function middlewareRSCLoader(this: any) {
const writer = transformStream.writable.getWriter()
const encoder = new TextEncoder()
renderToHTML(
{ url: url.pathname },
{},
url.pathname,
query,
renderOpts
).then(result => {
try {
const result = await renderToHTML(
{ url: url.pathname },
{},
url.pathname,
query,
renderOpts
)
result.pipe({
write: str => writer.write(encoder.encode(str)),
end: () => writer.close()
})
})
} catch (err) {
return new Response(
(err || 'An error occurred while rendering ' + url.pathname + '.').toString(),
{
status: 500,
headers: { 'x-middleware-ssr': '1' }
}
)
}
return new Response(transformStream.readable, {
headers: { 'x-middleware-ssr': '1' }
Expand Down
142 changes: 124 additions & 18 deletions packages/next/build/webpack/plugins/middleware-plugin.ts
@@ -1,4 +1,4 @@
import { webpack, sources } from 'next/dist/compiled/webpack/webpack'
import { webpack, sources, webpack5 } from 'next/dist/compiled/webpack/webpack'
import { getMiddlewareRegex } from '../../../shared/lib/router/utils'
import { getSortedRoutes } from '../../../shared/lib/router/utils'
import {
Expand Down Expand Up @@ -39,7 +39,7 @@ export default class MiddlewarePlugin {
}

createAssets(
compilation: any,
compilation: webpack5.Compilation,
assets: any,
envPerRoute: Map<string, string[]>
) {
Expand All @@ -52,6 +52,7 @@ export default class MiddlewarePlugin {
}

for (const entrypoint of entrypoints.values()) {
if (!entrypoint.name) continue
const result = MIDDLEWARE_FULL_ROUTE_REGEX.exec(entrypoint.name)
const ssrEntryInfo = ssrEntries.get(entrypoint.name)

Expand Down Expand Up @@ -111,19 +112,21 @@ export default class MiddlewarePlugin {
)
}

apply(compiler: webpack.Compiler) {
apply(compiler: webpack5.Compiler) {
const { dev } = this
const wp = compiler.webpack
compiler.hooks.compilation.tap(
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
const envPerRoute = new Map<string, string[]>()

compilation.hooks.finishModules.tap(PLUGIN_NAME, () => {
compilation.hooks.afterOptimizeModules.tap(PLUGIN_NAME, () => {
const { moduleGraph } = compilation as any
envPerRoute.clear()

for (const [name, info] of compilation.entries) {
if (name.match(MIDDLEWARE_ROUTE)) {
const middlewareEntries = new Set<webpack.Module>()
const middlewareEntries = new Set<webpack5.Module>()
const env = new Set<string>()

const addEntriesFromDependency = (dep: any) => {
Expand All @@ -133,19 +136,41 @@ export default class MiddlewarePlugin {
}
}

const runtime = wp.util.runtime.getEntryRuntime(compilation, name)

info.dependencies.forEach(addEntriesFromDependency)
info.includeDependencies.forEach(addEntriesFromDependency)

const queue = new Set(middlewareEntries)
for (const module of queue) {
const { buildInfo } = module as any
if (buildInfo?.usingIndirectEval) {
// @ts-ignore TODO: Remove ignore when webpack 5 is stable
const error = new webpack.WebpackError(
`\`eval\` not allowed in Middleware ${name}`
const { buildInfo } = module
if (
!dev &&
buildInfo &&
isUsedByExports({
module,
moduleGraph,
runtime,
usedByExports: buildInfo.usingIndirectEval,
})
) {
if (
/node_modules[\\/]regenerator-runtime[\\/]runtime\.js/.test(
module.identifier()
)
)
continue
const error = new wp.WebpackError(
`Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Middleware ${name}${
typeof buildInfo.usingIndirectEval !== 'boolean'
? `\nUsed by ${Array.from(
buildInfo.usingIndirectEval
).join(', ')}`
: ''
}`
)
error.module = module
compilation.warnings.push(error)
compilation.errors.push(error)
}

if (buildInfo?.nextUsedEnvVars !== undefined) {
Expand All @@ -167,19 +192,82 @@ export default class MiddlewarePlugin {
}
})

const handler = (parser: any) => {
const flagModule = () => {
parser.state.module.buildInfo.usingIndirectEval = true
const handler = (parser: webpack5.javascript.JavascriptParser) => {
const wrapExpression = (expr: any) => {
if (dev) {
const dep1 = new wp.dependencies.ConstDependency(
'__next_eval__(function() { return ',
expr.range[0]
)
dep1.loc = expr.loc
parser.state.module.addPresentationalDependency(dep1)
const dep2 = new wp.dependencies.ConstDependency(
'})',
expr.range[1]
)
dep2.loc = expr.loc
parser.state.module.addPresentationalDependency(dep2)
}
expressionHandler()
return true
}

const flagModule = (
usedByExports: boolean | Set<string> | undefined
) => {
if (usedByExports === undefined) usedByExports = true
const old = parser.state.module.buildInfo.usingIndirectEval
if (old === true || usedByExports === false) return
if (!old || usedByExports === true) {
parser.state.module.buildInfo.usingIndirectEval = usedByExports
return
}
const set = new Set(old)
for (const item of usedByExports) {
set.add(item)
}
parser.state.module.buildInfo.usingIndirectEval = set
}

const expressionHandler = () => {
wp.optimize.InnerGraph.onUsage(parser.state, flagModule)
}

const ignore = () => {
return true
}

parser.hooks.expression.for('eval').tap(PLUGIN_NAME, flagModule)
parser.hooks.expression.for('Function').tap(PLUGIN_NAME, flagModule)
// wrapping
parser.hooks.call.for('eval').tap(PLUGIN_NAME, wrapExpression)
parser.hooks.call.for('global.eval').tap(PLUGIN_NAME, wrapExpression)
parser.hooks.call.for('Function').tap(PLUGIN_NAME, wrapExpression)
parser.hooks.call
.for('global.Function')
.tap(PLUGIN_NAME, wrapExpression)
parser.hooks.new.for('Function').tap(PLUGIN_NAME, wrapExpression)
parser.hooks.new
.for('global.Function')
.tap(PLUGIN_NAME, wrapExpression)

// fallbacks
parser.hooks.expression
.for('eval')
.tap(PLUGIN_NAME, expressionHandler)
parser.hooks.expression
.for('Function')
.tap(PLUGIN_NAME, expressionHandler)
parser.hooks.expression
.for('Function.prototype')
.tap(PLUGIN_NAME, ignore)
parser.hooks.expression
.for('global.eval')
.tap(PLUGIN_NAME, flagModule)
.tap(PLUGIN_NAME, expressionHandler)
parser.hooks.expression
.for('global.Function')
.tap(PLUGIN_NAME, flagModule)
.tap(PLUGIN_NAME, expressionHandler)
parser.hooks.expression
.for('global.Function.prototype')
.tap(PLUGIN_NAME, ignore)

const memberChainHandler = (_expr: any, members: string[]) => {
if (
Expand Down Expand Up @@ -237,3 +325,21 @@ export default class MiddlewarePlugin {
)
}
}

function isUsedByExports(args: {
module: webpack5.Module
moduleGraph: webpack5.ModuleGraph
runtime: any
usedByExports: boolean | Set<string> | undefined
}): boolean {
const { moduleGraph, runtime, module, usedByExports } = args
if (usedByExports === undefined) return false
if (typeof usedByExports === 'boolean') return usedByExports
const exportsInfo = moduleGraph.getExportsInfo(module)
const wp = webpack as unknown as typeof webpack5
for (const exportName of usedByExports) {
if (exportsInfo.getUsed(exportName, runtime) !== wp.UsageState.Unused)
return true
}
return false
}

0 comments on commit 659f3b0

Please sign in to comment.