From d6c3a494c6769c5fc66293a2d0112a73625415fc Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Sun, 10 Jul 2022 19:53:08 +0300 Subject: [PATCH 01/16] [edge] allow importing blob assets --- .../webpack/plugins/middleware-plugin.ts | 14 ++++++++++ packages/next/server/body-streams.ts | 2 +- packages/next/server/next-server.ts | 2 ++ packages/next/server/web/sandbox/context.ts | 28 +++++++++++++++++-- packages/next/server/web/sandbox/sandbox.ts | 2 ++ .../app/pages/api/edge.js | 26 +++++++++++++++++ .../app/src/text-file.txt | 1 + .../index.test.ts | 23 +++++++++++++++ 8 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js create mode 100644 test/e2e/edge-compiler-can-import-blob-assets/app/src/text-file.txt create mode 100644 test/e2e/edge-compiler-can-import-blob-assets/index.test.ts diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 2f1587185fabd..bd60901a3777c 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -13,6 +13,7 @@ import { MIDDLEWARE_REACT_LOADABLE_MANIFEST, NEXT_CLIENT_SSR_ENTRY_SUFFIX, } from '../../../shared/lib/constants' +import { join } from 'path' interface EdgeFunctionDefinition { env: string[] @@ -21,6 +22,7 @@ interface EdgeFunctionDefinition { page: string regexp: string wasm?: WasmBinding[] + blobs: WasmBinding[] } export interface MiddlewareManifest { @@ -36,6 +38,7 @@ interface EntryMetadata { edgeSSR?: EdgeSSRMeta env: Set wasmBindings: Set + blobBindings: Set } const NAME = 'MiddlewarePlugin' @@ -411,6 +414,7 @@ function getExtractMetadata(params: { const entryMetadata: EntryMetadata = { env: new Set(), wasmBindings: new Set(), + blobBindings: new Set(), } for (const entryModule of entryModules) { @@ -482,6 +486,10 @@ function getExtractMetadata(params: { entryMetadata.wasmBindings.add(buildInfo.nextWasmMiddlewareBinding) } + if (entryModule.type === 'asset/resource') { + entryMetadata.blobBindings.add(entryModule) + } + /** * Append to the list of modules to process outgoingConnections from * the module that is being processed. @@ -558,6 +566,12 @@ function getCreateAssets(params: { page: page, regexp, wasm: Array.from(metadata.wasmBindings), + blobs: Array.from(metadata.blobBindings, (mod) => { + return { + filePath: join('server', mod.buildInfo.filename), + name: mod.buildInfo.fullContentHash, + } + }), } if (metadata.edgeApiFunction || metadata.edgeSSR) { diff --git a/packages/next/server/body-streams.ts b/packages/next/server/body-streams.ts index 4c6feee1986d4..d9afc65d265d9 100644 --- a/packages/next/server/body-streams.ts +++ b/packages/next/server/body-streams.ts @@ -7,7 +7,7 @@ type BodyStream = ReadableStream /** * Creates a ReadableStream from a Node.js HTTP request */ -export function requestToBodyStream(request: IncomingMessage): BodyStream { +export function requestToBodyStream(request: Readable): BodyStream { const transform = new Primitives.TransformStream({ start(controller) { request.on('data', (chunk) => controller.enqueue(chunk)) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 4fcef3be4d1fd..e0db4c6e341e8 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -1223,6 +1223,7 @@ export default class NextNodeServer extends BaseServer { } result = await run({ + distDir: this.distDir, name: middlewareInfo.name, paths: middlewareInfo.paths, env: middlewareInfo.env, @@ -1552,6 +1553,7 @@ export default class NextNodeServer extends BaseServer { const nodeReq = params.req as NodeNextRequest const result = await run({ + distDir: this.distDir, name: middlewareInfo.name, paths: middlewareInfo.paths, env: middlewareInfo.env, diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index 05cf16cce106a..a7f549e9c6b10 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -6,9 +6,11 @@ import { } from 'next/dist/compiled/@next/react-dev-overlay/dist/middleware' import { EDGE_UNSUPPORTED_NODE_APIS } from '../../../shared/lib/constants' import { EdgeRuntime } from 'next/dist/compiled/edge-runtime' -import { readFileSync, promises as fs } from 'fs' +import { readFileSync, createReadStream, promises as fs } from 'fs' import { validateURL } from '../utils' import { pick } from '../../../lib/pick' +import path from 'path' +import { requestToBodyStream } from '../../body-streams' const WEBPACK_HASH_REGEX = /__webpack_require__\.h = function\(\) \{ return "[0-9a-f]+"; \}/g @@ -49,6 +51,7 @@ interface ModuleContextOptions { useCache: boolean env: string[] wasm: WasmBinding[] + distDir: string } const pendingModuleCaches = new Map>() @@ -104,6 +107,7 @@ async function createModuleContext(options: ModuleContextOptions) { const warnedEvals = new Set() const warnedWasmCodegens = new Set() const wasm = await loadWasm(options.wasm) + console.log({ cwd: process.cwd(), wasm: options.wasm }) const runtime = new EdgeRuntime({ codeGeneration: process.env.NODE_ENV !== 'production' @@ -197,7 +201,7 @@ Learn More: https://nextjs.org/docs/messages/middleware-dynamic-wasm-compilation } const __fetch = context.fetch - context.fetch = (input: RequestInfo, init: RequestInit = {}) => { + context.fetch = async (input: RequestInfo, init: RequestInit = {}) => { init.headers = new Headers(init.headers ?? {}) const prevs = init.headers.get(`x-middleware-subrequest`)?.split(':') || [] @@ -208,6 +212,26 @@ Learn More: https://nextjs.org/docs/messages/middleware-dynamic-wasm-compilation init.headers.set(`user-agent`, `Next.js Middleware`) } + const inputString = String(input) + if (inputString.startsWith('/_next/static/media/')) { + const pathname = inputString.replace('/_next/static/media/', '') + const fullPathname = path.join( + options.distDir, + `server/static/media/${pathname}` + ) + if ( + await fs.access(fullPathname).then( + () => true, + () => false + ) + ) { + const blob = createReadStream(fullPathname) + return new context.Response(requestToBodyStream(blob)) + } + } else if (inputString.startsWith('file://')) { + return new Response('Not Found', { status: 404 }) + } + if (typeof input === 'object' && 'url' in input) { return __fetch(input.url, { ...pick(input, [ diff --git a/packages/next/server/web/sandbox/sandbox.ts b/packages/next/server/web/sandbox/sandbox.ts index 3578fd1024f91..4f18ed6512ec4 100644 --- a/packages/next/server/web/sandbox/sandbox.ts +++ b/packages/next/server/web/sandbox/sandbox.ts @@ -13,6 +13,7 @@ type RunnerFn = (params: { request: RequestData useCache: boolean wasm: WasmBinding[] + distDir: string }) => Promise export const run = withTaggedErrors(async (params) => { @@ -22,6 +23,7 @@ export const run = withTaggedErrors(async (params) => { useCache: params.useCache !== false, env: params.env, wasm: params.wasm, + distDir: params.distDir, }) for (const paramPath of params.paths) { diff --git a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js new file mode 100644 index 0000000000000..2118b3fa367b4 --- /dev/null +++ b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js @@ -0,0 +1,26 @@ +export const config = { runtime: 'experimental-edge' } + +/** + * @param {import('next/server').NextRequest} req + */ +export default async (req) => { + const handlerName = req.nextUrl.searchParams.get('handler') + const handler = handlers.get(handlerName) || defaultHandler + return handler() +} + +/** + * @type {Map Promise>} + */ +const handlers = new Map([ + [ + 'text-file', + async () => { + const url = new URL('../../src/text-file.txt', import.meta.url) + return fetch(url) + }, + ], +]) + +const defaultHandler = async () => + new Response('Invalid handler', { status: 400 }) diff --git a/test/e2e/edge-compiler-can-import-blob-assets/app/src/text-file.txt b/test/e2e/edge-compiler-can-import-blob-assets/app/src/text-file.txt new file mode 100644 index 0000000000000..37607fa1b1ec0 --- /dev/null +++ b/test/e2e/edge-compiler-can-import-blob-assets/app/src/text-file.txt @@ -0,0 +1 @@ +Hello, from text-file.txt! diff --git a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts new file mode 100644 index 0000000000000..4f89773cc521b --- /dev/null +++ b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts @@ -0,0 +1,23 @@ +import { createNext, FileRef } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { renderViaHTTP } from 'next-test-utils' +import path from 'path' + +describe('Edge Compiler can import blob assets', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: new FileRef(path.join(__dirname, './app')), + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('allows to fetch the blobs', async () => { + const html = await renderViaHTTP(next.url, '/api/edge', { + handler: 'text-file', + }) + expect(html).toContain('Hello, from text-file.txt!') + }) +}) From e614a7fa09f5dbf06f2ec2057d12d46734516598 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 11 Jul 2022 09:32:26 +0300 Subject: [PATCH 02/16] Fix test --- test/e2e/middleware-general/test/index.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/middleware-general/test/index.test.ts b/test/e2e/middleware-general/test/index.test.ts index 92543d2b9fbb0..52018cdf45478 100644 --- a/test/e2e/middleware-general/test/index.test.ts +++ b/test/e2e/middleware-general/test/index.test.ts @@ -114,6 +114,7 @@ describe('Middleware Runtime', () => { page: '/', regexp: '^/.*$', wasm: [], + blobs: [], }, }) }) From f51f1aecf21a2166b68905963d433417b0179927 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 11 Jul 2022 09:47:39 +0300 Subject: [PATCH 03/16] extract to a new file, to make it easier to read and review --- packages/next/server/web/sandbox/context.ts | 31 ++++------- .../server/web/sandbox/fetch-inline-blobs.ts | 51 +++++++++++++++++++ 2 files changed, 61 insertions(+), 21 deletions(-) create mode 100644 packages/next/server/web/sandbox/fetch-inline-blobs.ts diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index a7f549e9c6b10..95931b36ed032 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -11,6 +11,7 @@ import { validateURL } from '../utils' import { pick } from '../../../lib/pick' import path from 'path' import { requestToBodyStream } from '../../body-streams' +import { fetchInlineBlob } from './fetch-inline-blobs' const WEBPACK_HASH_REGEX = /__webpack_require__\.h = function\(\) \{ return "[0-9a-f]+"; \}/g @@ -107,7 +108,6 @@ async function createModuleContext(options: ModuleContextOptions) { const warnedEvals = new Set() const warnedWasmCodegens = new Set() const wasm = await loadWasm(options.wasm) - console.log({ cwd: process.cwd(), wasm: options.wasm }) const runtime = new EdgeRuntime({ codeGeneration: process.env.NODE_ENV !== 'production' @@ -202,6 +202,15 @@ Learn More: https://nextjs.org/docs/messages/middleware-dynamic-wasm-compilation const __fetch = context.fetch context.fetch = async (input: RequestInfo, init: RequestInit = {}) => { + const blobResponse = await fetchInlineBlob({ + input, + distDir: options.distDir, + context, + }) + if (blobResponse) { + return blobResponse + } + init.headers = new Headers(init.headers ?? {}) const prevs = init.headers.get(`x-middleware-subrequest`)?.split(':') || [] @@ -212,26 +221,6 @@ Learn More: https://nextjs.org/docs/messages/middleware-dynamic-wasm-compilation init.headers.set(`user-agent`, `Next.js Middleware`) } - const inputString = String(input) - if (inputString.startsWith('/_next/static/media/')) { - const pathname = inputString.replace('/_next/static/media/', '') - const fullPathname = path.join( - options.distDir, - `server/static/media/${pathname}` - ) - if ( - await fs.access(fullPathname).then( - () => true, - () => false - ) - ) { - const blob = createReadStream(fullPathname) - return new context.Response(requestToBodyStream(blob)) - } - } else if (inputString.startsWith('file://')) { - return new Response('Not Found', { status: 404 }) - } - if (typeof input === 'object' && 'url' in input) { return __fetch(input.url, { ...pick(input, [ diff --git a/packages/next/server/web/sandbox/fetch-inline-blobs.ts b/packages/next/server/web/sandbox/fetch-inline-blobs.ts new file mode 100644 index 0000000000000..757485e2cb6f6 --- /dev/null +++ b/packages/next/server/web/sandbox/fetch-inline-blobs.ts @@ -0,0 +1,51 @@ +import { createReadStream, promises as fs } from 'fs' +import path from 'path' +import { requestToBodyStream } from '../../body-streams' + +/** + * Short-circuits the `fetch` function + * to return a stream for a given blob, if a user used `new URL("file", import.meta.url)`. + * This allows to embed blobs in Edge Runtime. + */ +export async function fetchInlineBlob(options: { + input: RequestInfo + distDir: string + context: { Response: any } +}): Promise { + const inputString = String(options.input) + + /** + * `file://` URLs should always fail to load + */ + if (inputString.startsWith('file://')) { + return new Response('Not Found', { status: 404 }) + } else if (!inputString.startsWith('/_next/static/media/')) { + return + } + + const rootMediaDirectory = path.resolve( + options.distDir, + 'server/static/media' + ) + const pathname = inputString.replace('/_next/static/media/', '') + const fullPathname = path.resolve(rootMediaDirectory, pathname) + + if (isPathUnderRoot({ root: rootMediaDirectory, path: fullPathname })) { + return + } + + const fileIsReadable = await fs.access(fullPathname).then( + () => true, + () => false + ) + + if (fileIsReadable) { + const blob = createReadStream(fullPathname) + return new options.context.Response(requestToBodyStream(blob)) + } +} + +function isPathUnderRoot(options: { root: string; path: string }): boolean { + const relative = path.relative(options.root, options.path) + return !relative || relative.startsWith('..') || path.isAbsolute(relative) +} From 52130d6a958e90442cb397f6cc658e0c4c6e0699 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 11 Jul 2022 15:00:10 +0300 Subject: [PATCH 04/16] Use webpack asset discovery and transform with a loader --- packages/next/build/webpack-config.ts | 7 ++++ .../webpack/loaders/get-module-build-info.ts | 11 ++++++ .../loaders/next-middleware-asset-loader.ts | 20 ++++++++++ .../webpack/plugins/middleware-plugin.ts | 35 +++++++++++------- packages/next/server/next-server.ts | 10 ++++- packages/next/server/web/sandbox/context.ts | 10 ++--- .../server/web/sandbox/fetch-inline-blobs.ts | 32 +++++----------- packages/next/server/web/sandbox/sandbox.ts | 6 +-- .../app/pages/api/edge.js | 7 ++++ .../app/src/vercel.png | Bin 0 -> 30079 bytes .../index.test.ts | 16 +++++++- 11 files changed, 106 insertions(+), 48 deletions(-) create mode 100644 packages/next/build/webpack/loaders/next-middleware-asset-loader.ts create mode 100644 test/e2e/edge-compiler-can-import-blob-assets/app/src/vercel.png diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index c2f0c3d11a1cc..4d3a2531dea3e 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1272,6 +1272,7 @@ export default async function getBaseWebpackConfig( 'next-middleware-loader', 'next-edge-function-loader', 'next-edge-ssr-loader', + 'next-middleware-asset-loader', 'next-middleware-wasm-loader', 'next-app-loader', ].reduce((alias, loader) => { @@ -1769,6 +1770,12 @@ export default async function getBaseWebpackConfig( type: 'javascript/auto', resourceQuery: /module/i, }) + webpack5Config.module?.rules?.unshift({ + dependency: 'url', + loader: 'next-middleware-asset-loader', + type: 'javascript/auto', + layer: 'edge-asset', + }) } webpack5Config.experiments = { diff --git a/packages/next/build/webpack/loaders/get-module-build-info.ts b/packages/next/build/webpack/loaders/get-module-build-info.ts index d6730864cc96c..0d5fcb84cc483 100644 --- a/packages/next/build/webpack/loaders/get-module-build-info.ts +++ b/packages/next/build/webpack/loaders/get-module-build-info.ts @@ -11,6 +11,7 @@ export function getModuleBuildInfo(webpackModule: webpack5.Module) { nextEdgeSSR?: EdgeSSRMeta nextUsedEnvVars?: Set nextWasmMiddlewareBinding?: WasmBinding + nextAssetMiddlewareBinding?: AssetBinding usingIndirectEval?: boolean | Set route?: RouteMeta importLocByPath?: Map @@ -36,3 +37,13 @@ export interface WasmBinding { filePath: string name: string } + +export interface AssetBinding { + filePath: string + name: string + /** + * this.emitFile fails so I add the source here to add an asset + * directly in the hook. Maybe I can fix it + */ + source: Buffer +} diff --git a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts new file mode 100644 index 0000000000000..31f86df70ebd0 --- /dev/null +++ b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts @@ -0,0 +1,20 @@ +import { getModuleBuildInfo } from './get-module-build-info' +import crypto from 'crypto' + +export default function MiddlewareAssetLoader(this: any, source: Buffer) { + const name = `asset_${sha1(source)}` + const filePath = `edge-chunks/${name}` + const buildInfo = getModuleBuildInfo(this._module) + buildInfo.nextAssetMiddlewareBinding = { + filePath: `server/${filePath}`, + name, + source, + } + return `module.exports = ${JSON.stringify(`blob:${name}`)}` +} + +export const raw = true + +function sha1(source: string | Buffer) { + return crypto.createHash('sha1').update(source).digest('hex') +} diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index bd60901a3777c..1e281409cfe33 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -1,4 +1,7 @@ -import type { EdgeMiddlewareMeta } from '../loaders/get-module-build-info' +import { + AssetBinding, + EdgeMiddlewareMeta, +} from '../loaders/get-module-build-info' import type { EdgeSSRMeta, WasmBinding } from '../loaders/get-module-build-info' import { getNamedMiddlewareRegex } from '../../../shared/lib/router/utils/route-regex' import { getModuleBuildInfo } from '../loaders/get-module-build-info' @@ -13,16 +16,15 @@ import { MIDDLEWARE_REACT_LOADABLE_MANIFEST, NEXT_CLIENT_SSR_ENTRY_SUFFIX, } from '../../../shared/lib/constants' -import { join } from 'path' -interface EdgeFunctionDefinition { +export interface EdgeFunctionDefinition { env: string[] files: string[] name: string page: string regexp: string wasm?: WasmBinding[] - blobs: WasmBinding[] + assets?: Omit[] } export interface MiddlewareManifest { @@ -38,7 +40,7 @@ interface EntryMetadata { edgeSSR?: EdgeSSRMeta env: Set wasmBindings: Set - blobBindings: Set + assetBindings: Set } const NAME = 'MiddlewarePlugin' @@ -414,7 +416,7 @@ function getExtractMetadata(params: { const entryMetadata: EntryMetadata = { env: new Set(), wasmBindings: new Set(), - blobBindings: new Set(), + assetBindings: new Set(), } for (const entryModule of entryModules) { @@ -486,8 +488,8 @@ function getExtractMetadata(params: { entryMetadata.wasmBindings.add(buildInfo.nextWasmMiddlewareBinding) } - if (entryModule.type === 'asset/resource') { - entryMetadata.blobBindings.add(entryModule) + if (buildInfo?.nextAssetMiddlewareBinding) { + entryMetadata.assetBindings.add(buildInfo.nextAssetMiddlewareBinding) } /** @@ -559,6 +561,13 @@ function getCreateAssets(params: { }) const regexp = metadata?.edgeMiddleware?.matcherRegexp || namedRegex + for (const asset of metadata.assetBindings) { + assets[asset.filePath.replace('server/', '')] = new sources.RawSource( + // it is allowed to provide a Buffer + asset.source as unknown as string + ) + } + const edgeFunctionDefinition: EdgeFunctionDefinition = { env: Array.from(metadata.env), files: getEntryFiles(entrypoint.getFiles(), metadata), @@ -566,12 +575,10 @@ function getCreateAssets(params: { page: page, regexp, wasm: Array.from(metadata.wasmBindings), - blobs: Array.from(metadata.blobBindings, (mod) => { - return { - filePath: join('server', mod.buildInfo.filename), - name: mod.buildInfo.fullContentHash, - } - }), + assets: Array.from(metadata.assetBindings, (v) => ({ + name: v.name, + filePath: v.filePath, + })), } if (metadata.edgeApiFunction || metadata.edgeSSR) { diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index e0db4c6e341e8..f3e4ec22c14b2 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -1121,6 +1121,12 @@ export default class NextNodeServer extends BaseServer { ...binding, filePath: join(this.distDir, binding.filePath), })), + assets: (pageInfo.assets ?? []).map((binding) => { + return { + ...binding, + filePath: join(this.distDir, binding.filePath), + } + }), } } @@ -1227,7 +1233,7 @@ export default class NextNodeServer extends BaseServer { name: middlewareInfo.name, paths: middlewareInfo.paths, env: middlewareInfo.env, - wasm: middlewareInfo.wasm, + edgeFunctionEntry: middlewareInfo, request: { headers: params.request.headers, method, @@ -1557,7 +1563,7 @@ export default class NextNodeServer extends BaseServer { name: middlewareInfo.name, paths: middlewareInfo.paths, env: middlewareInfo.env, - wasm: middlewareInfo.wasm, + edgeFunctionEntry: middlewareInfo, request: { headers: params.req.headers, method: params.req.method, diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index 95931b36ed032..e270b4d09ffba 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -6,12 +6,11 @@ import { } from 'next/dist/compiled/@next/react-dev-overlay/dist/middleware' import { EDGE_UNSUPPORTED_NODE_APIS } from '../../../shared/lib/constants' import { EdgeRuntime } from 'next/dist/compiled/edge-runtime' -import { readFileSync, createReadStream, promises as fs } from 'fs' +import { readFileSync, promises as fs } from 'fs' import { validateURL } from '../utils' import { pick } from '../../../lib/pick' -import path from 'path' -import { requestToBodyStream } from '../../body-streams' import { fetchInlineBlob } from './fetch-inline-blobs' +import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin' const WEBPACK_HASH_REGEX = /__webpack_require__\.h = function\(\) \{ return "[0-9a-f]+"; \}/g @@ -51,8 +50,8 @@ interface ModuleContextOptions { onWarning: (warn: Error) => void useCache: boolean env: string[] - wasm: WasmBinding[] distDir: string + edgeFunctionEntry: Pick } const pendingModuleCaches = new Map>() @@ -107,7 +106,7 @@ export async function getModuleContext(options: ModuleContextOptions) { async function createModuleContext(options: ModuleContextOptions) { const warnedEvals = new Set() const warnedWasmCodegens = new Set() - const wasm = await loadWasm(options.wasm) + const wasm = await loadWasm(options.edgeFunctionEntry.wasm ?? []) const runtime = new EdgeRuntime({ codeGeneration: process.env.NODE_ENV !== 'production' @@ -204,6 +203,7 @@ Learn More: https://nextjs.org/docs/messages/middleware-dynamic-wasm-compilation context.fetch = async (input: RequestInfo, init: RequestInit = {}) => { const blobResponse = await fetchInlineBlob({ input, + assets: options.edgeFunctionEntry.assets, distDir: options.distDir, context, }) diff --git a/packages/next/server/web/sandbox/fetch-inline-blobs.ts b/packages/next/server/web/sandbox/fetch-inline-blobs.ts index 757485e2cb6f6..f2799e500f6ae 100644 --- a/packages/next/server/web/sandbox/fetch-inline-blobs.ts +++ b/packages/next/server/web/sandbox/fetch-inline-blobs.ts @@ -1,5 +1,6 @@ import { createReadStream, promises as fs } from 'fs' import path from 'path' +import { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin' import { requestToBodyStream } from '../../body-streams' /** @@ -10,42 +11,29 @@ import { requestToBodyStream } from '../../body-streams' export async function fetchInlineBlob(options: { input: RequestInfo distDir: string + assets: EdgeFunctionDefinition['assets'] context: { Response: any } }): Promise { const inputString = String(options.input) - - /** - * `file://` URLs should always fail to load - */ - if (inputString.startsWith('file://')) { - return new Response('Not Found', { status: 404 }) - } else if (!inputString.startsWith('/_next/static/media/')) { + if (!inputString.startsWith('blob:')) { return } - const rootMediaDirectory = path.resolve( - options.distDir, - 'server/static/media' - ) - const pathname = inputString.replace('/_next/static/media/', '') - const fullPathname = path.resolve(rootMediaDirectory, pathname) - - if (isPathUnderRoot({ root: rootMediaDirectory, path: fullPathname })) { + const hash = inputString.replace('blob:', '') + const asset = options.assets?.find((x) => x.name === hash) + if (!asset) { return } - const fileIsReadable = await fs.access(fullPathname).then( + const filePath = path.resolve(options.distDir, asset.filePath) + + const fileIsReadable = await fs.access(filePath).then( () => true, () => false ) if (fileIsReadable) { - const blob = createReadStream(fullPathname) + const blob = createReadStream(filePath) return new options.context.Response(requestToBodyStream(blob)) } } - -function isPathUnderRoot(options: { root: string; path: string }): boolean { - const relative = path.relative(options.root, options.path) - return !relative || relative.startsWith('..') || path.isAbsolute(relative) -} diff --git a/packages/next/server/web/sandbox/sandbox.ts b/packages/next/server/web/sandbox/sandbox.ts index 4f18ed6512ec4..0390a8f12275d 100644 --- a/packages/next/server/web/sandbox/sandbox.ts +++ b/packages/next/server/web/sandbox/sandbox.ts @@ -1,7 +1,7 @@ import type { RequestData, FetchEventResult } from '../types' -import type { WasmBinding } from '../../../build/webpack/loaders/get-module-build-info' import { getServerError } from 'next/dist/compiled/@next/react-dev-overlay/dist/middleware' import { getModuleContext } from './context' +import { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin' export const ErrorSource = Symbol('SandboxError') @@ -12,7 +12,7 @@ type RunnerFn = (params: { paths: string[] request: RequestData useCache: boolean - wasm: WasmBinding[] + edgeFunctionEntry: Pick distDir: string }) => Promise @@ -22,7 +22,7 @@ export const run = withTaggedErrors(async (params) => { onWarning: params.onWarning, useCache: params.useCache !== false, env: params.env, - wasm: params.wasm, + edgeFunctionEntry: params.edgeFunctionEntry, distDir: params.distDir, }) diff --git a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js index 2118b3fa367b4..d4e8133f1df61 100644 --- a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js +++ b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js @@ -20,6 +20,13 @@ const handlers = new Map([ return fetch(url) }, ], + [ + 'image-file', + async () => { + const url = new URL('../../src/vercel.png', import.meta.url) + return fetch(url) + }, + ], ]) const defaultHandler = async () => diff --git a/test/e2e/edge-compiler-can-import-blob-assets/app/src/vercel.png b/test/e2e/edge-compiler-can-import-blob-assets/app/src/vercel.png new file mode 100644 index 0000000000000000000000000000000000000000..cb137a989e5ffe5fe01eee70a4ad7a6e1798094a GIT binary patch literal 30079 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 { let next: NextInstance @@ -14,10 +15,21 @@ describe('Edge Compiler can import blob assets', () => { }) afterAll(() => next.destroy()) - it('allows to fetch the blobs', async () => { + it('allows to fetch text blobs', async () => { const html = await renderViaHTTP(next.url, '/api/edge', { handler: 'text-file', }) expect(html).toContain('Hello, from text-file.txt!') }) + + it('allows to fetch image blobs', async () => { + const response = await fetchViaHTTP(next.url, '/api/edge', { + handler: 'image-file', + }) + const buffer: Buffer = await response.buffer() + const image = await fs.readFile( + path.join(__dirname, './app/src/vercel.png') + ) + expect(buffer.equals(image)).toBeTrue() + }) }) From c2a38b5dfc11b5b557a20ae130c94677b48239c1 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 11 Jul 2022 15:02:35 +0300 Subject: [PATCH 05/16] fix tests --- test/e2e/middleware-general/test/index.test.ts | 2 +- test/e2e/middleware-trailing-slash/test/index.test.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/middleware-general/test/index.test.ts b/test/e2e/middleware-general/test/index.test.ts index 52018cdf45478..1717eaa8c5ec3 100644 --- a/test/e2e/middleware-general/test/index.test.ts +++ b/test/e2e/middleware-general/test/index.test.ts @@ -114,7 +114,7 @@ describe('Middleware Runtime', () => { page: '/', regexp: '^/.*$', wasm: [], - blobs: [], + assets: [], }, }) }) diff --git a/test/e2e/middleware-trailing-slash/test/index.test.ts b/test/e2e/middleware-trailing-slash/test/index.test.ts index 09fcf93431f1a..4277ec9ae80f7 100644 --- a/test/e2e/middleware-trailing-slash/test/index.test.ts +++ b/test/e2e/middleware-trailing-slash/test/index.test.ts @@ -61,6 +61,7 @@ describe('Middleware Runtime trailing slash', () => { page: '/', regexp: '^/.*$', wasm: [], + assets: [], }, }) }) From d118710fb1e740e16c9795aabe378378c0a2aeb5 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 11 Jul 2022 15:03:25 +0300 Subject: [PATCH 06/16] don't prefix assets --- .../build/webpack/loaders/next-middleware-asset-loader.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts index 31f86df70ebd0..32093ab5397da 100644 --- a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts +++ b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts @@ -2,8 +2,8 @@ import { getModuleBuildInfo } from './get-module-build-info' import crypto from 'crypto' export default function MiddlewareAssetLoader(this: any, source: Buffer) { - const name = `asset_${sha1(source)}` - const filePath = `edge-chunks/${name}` + const name = sha1(source) + const filePath = `edge-chunks/asset_${name}` const buildInfo = getModuleBuildInfo(this._module) buildInfo.nextAssetMiddlewareBinding = { filePath: `server/${filePath}`, From 163d27ba2f800aefb8f945e4c6375541966952eb Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 11 Jul 2022 15:09:25 +0300 Subject: [PATCH 07/16] use emitFile --- packages/next/build/webpack-config.ts | 4 ++++ .../build/webpack/loaders/get-module-build-info.ts | 5 ----- .../loaders/next-middleware-asset-loader.ts | 2 +- .../build/webpack/plugins/middleware-plugin.ts | 14 ++------------ 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 4d3a2531dea3e..fd4fa321bbd20 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1776,6 +1776,10 @@ export default async function getBaseWebpackConfig( type: 'javascript/auto', layer: 'edge-asset', }) + webpack5Config.module?.rules?.unshift({ + issuerLayer: 'edge-asset', + type: 'asset/source', + }) } webpack5Config.experiments = { diff --git a/packages/next/build/webpack/loaders/get-module-build-info.ts b/packages/next/build/webpack/loaders/get-module-build-info.ts index 0d5fcb84cc483..162a5be89a711 100644 --- a/packages/next/build/webpack/loaders/get-module-build-info.ts +++ b/packages/next/build/webpack/loaders/get-module-build-info.ts @@ -41,9 +41,4 @@ export interface WasmBinding { export interface AssetBinding { filePath: string name: string - /** - * this.emitFile fails so I add the source here to add an asset - * directly in the hook. Maybe I can fix it - */ - source: Buffer } diff --git a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts index 32093ab5397da..773c50cca6af5 100644 --- a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts +++ b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts @@ -8,8 +8,8 @@ export default function MiddlewareAssetLoader(this: any, source: Buffer) { buildInfo.nextAssetMiddlewareBinding = { filePath: `server/${filePath}`, name, - source, } + this.emitFile(filePath, source) return `module.exports = ${JSON.stringify(`blob:${name}`)}` } diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 1e281409cfe33..4b7b625a5d194 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -24,7 +24,7 @@ export interface EdgeFunctionDefinition { page: string regexp: string wasm?: WasmBinding[] - assets?: Omit[] + assets?: AssetBinding[] } export interface MiddlewareManifest { @@ -561,13 +561,6 @@ function getCreateAssets(params: { }) const regexp = metadata?.edgeMiddleware?.matcherRegexp || namedRegex - for (const asset of metadata.assetBindings) { - assets[asset.filePath.replace('server/', '')] = new sources.RawSource( - // it is allowed to provide a Buffer - asset.source as unknown as string - ) - } - const edgeFunctionDefinition: EdgeFunctionDefinition = { env: Array.from(metadata.env), files: getEntryFiles(entrypoint.getFiles(), metadata), @@ -575,10 +568,7 @@ function getCreateAssets(params: { page: page, regexp, wasm: Array.from(metadata.wasmBindings), - assets: Array.from(metadata.assetBindings, (v) => ({ - name: v.name, - filePath: v.filePath, - })), + assets: Array.from(metadata.assetBindings), } if (metadata.edgeApiFunction || metadata.edgeSSR) { From 2172eb13ed8b3d8baa4e2f87812ebd550e244322 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 11 Jul 2022 17:27:28 +0300 Subject: [PATCH 08/16] rename assets to blobs to be more specific --- .../webpack/loaders/get-module-build-info.ts | 9 ++------ .../loaders/next-middleware-asset-loader.ts | 2 +- .../webpack/plugins/middleware-plugin.ts | 22 +++++++++---------- packages/next/server/next-server.ts | 2 +- packages/next/server/web/sandbox/context.ts | 8 +++---- .../server/web/sandbox/fetch-inline-blobs.ts | 2 +- packages/next/server/web/sandbox/sandbox.ts | 2 +- .../e2e/middleware-general/test/index.test.ts | 2 +- .../test/index.test.ts | 2 +- 9 files changed, 23 insertions(+), 28 deletions(-) diff --git a/packages/next/build/webpack/loaders/get-module-build-info.ts b/packages/next/build/webpack/loaders/get-module-build-info.ts index 162a5be89a711..022292225273c 100644 --- a/packages/next/build/webpack/loaders/get-module-build-info.ts +++ b/packages/next/build/webpack/loaders/get-module-build-info.ts @@ -10,8 +10,8 @@ export function getModuleBuildInfo(webpackModule: webpack5.Module) { nextEdgeApiFunction?: EdgeMiddlewareMeta nextEdgeSSR?: EdgeSSRMeta nextUsedEnvVars?: Set - nextWasmMiddlewareBinding?: WasmBinding - nextAssetMiddlewareBinding?: AssetBinding + nextWasmMiddlewareBinding?: AssetBinding + nextBlobMiddlewareBinding?: AssetBinding usingIndirectEval?: boolean | Set route?: RouteMeta importLocByPath?: Map @@ -33,11 +33,6 @@ export interface EdgeSSRMeta { page: string } -export interface WasmBinding { - filePath: string - name: string -} - export interface AssetBinding { filePath: string name: string diff --git a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts index 773c50cca6af5..dc1b37d0b2f0b 100644 --- a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts +++ b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts @@ -5,7 +5,7 @@ export default function MiddlewareAssetLoader(this: any, source: Buffer) { const name = sha1(source) const filePath = `edge-chunks/asset_${name}` const buildInfo = getModuleBuildInfo(this._module) - buildInfo.nextAssetMiddlewareBinding = { + buildInfo.nextBlobMiddlewareBinding = { filePath: `server/${filePath}`, name, } diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 4b7b625a5d194..c1db74f148835 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -1,8 +1,8 @@ -import { +import type { AssetBinding, EdgeMiddlewareMeta, } from '../loaders/get-module-build-info' -import type { EdgeSSRMeta, WasmBinding } from '../loaders/get-module-build-info' +import type { EdgeSSRMeta } from '../loaders/get-module-build-info' import { getNamedMiddlewareRegex } from '../../../shared/lib/router/utils/route-regex' import { getModuleBuildInfo } from '../loaders/get-module-build-info' import { getSortedRoutes } from '../../../shared/lib/router/utils' @@ -23,8 +23,8 @@ export interface EdgeFunctionDefinition { name: string page: string regexp: string - wasm?: WasmBinding[] - assets?: AssetBinding[] + wasm?: AssetBinding[] + blobs?: AssetBinding[] } export interface MiddlewareManifest { @@ -39,8 +39,8 @@ interface EntryMetadata { edgeApiFunction?: EdgeMiddlewareMeta edgeSSR?: EdgeSSRMeta env: Set - wasmBindings: Set - assetBindings: Set + wasmBindings: Set + blobBindings: Set } const NAME = 'MiddlewarePlugin' @@ -415,8 +415,8 @@ function getExtractMetadata(params: { const entryMetadata: EntryMetadata = { env: new Set(), - wasmBindings: new Set(), - assetBindings: new Set(), + wasmBindings: new Set(), + blobBindings: new Set(), } for (const entryModule of entryModules) { @@ -488,8 +488,8 @@ function getExtractMetadata(params: { entryMetadata.wasmBindings.add(buildInfo.nextWasmMiddlewareBinding) } - if (buildInfo?.nextAssetMiddlewareBinding) { - entryMetadata.assetBindings.add(buildInfo.nextAssetMiddlewareBinding) + if (buildInfo?.nextBlobMiddlewareBinding) { + entryMetadata.blobBindings.add(buildInfo.nextBlobMiddlewareBinding) } /** @@ -568,7 +568,7 @@ function getCreateAssets(params: { page: page, regexp, wasm: Array.from(metadata.wasmBindings), - assets: Array.from(metadata.assetBindings), + blobs: Array.from(metadata.blobBindings), } if (metadata.edgeApiFunction || metadata.edgeSSR) { diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index f3e4ec22c14b2..9c890b73e0a2b 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -1121,7 +1121,7 @@ export default class NextNodeServer extends BaseServer { ...binding, filePath: join(this.distDir, binding.filePath), })), - assets: (pageInfo.assets ?? []).map((binding) => { + blobs: (pageInfo.blobs ?? []).map((binding) => { return { ...binding, filePath: join(this.distDir, binding.filePath), diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index e270b4d09ffba..8f8ac2fda5a01 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -1,5 +1,5 @@ import type { Primitives } from 'next/dist/compiled/@edge-runtime/primitives' -import type { WasmBinding } from '../../../build/webpack/loaders/get-module-build-info' +import type { AssetBinding } from '../../../build/webpack/loaders/get-module-build-info' import { decorateServerError, getServerError, @@ -51,7 +51,7 @@ interface ModuleContextOptions { useCache: boolean env: string[] distDir: string - edgeFunctionEntry: Pick + edgeFunctionEntry: Pick } const pendingModuleCaches = new Map>() @@ -203,7 +203,7 @@ Learn More: https://nextjs.org/docs/messages/middleware-dynamic-wasm-compilation context.fetch = async (input: RequestInfo, init: RequestInit = {}) => { const blobResponse = await fetchInlineBlob({ input, - assets: options.edgeFunctionEntry.assets, + assets: options.edgeFunctionEntry.blobs, distDir: options.distDir, context, }) @@ -283,7 +283,7 @@ Learn More: https://nextjs.org/docs/messages/middleware-dynamic-wasm-compilation } async function loadWasm( - wasm: WasmBinding[] + wasm: AssetBinding[] ): Promise> { const modules: Record = {} diff --git a/packages/next/server/web/sandbox/fetch-inline-blobs.ts b/packages/next/server/web/sandbox/fetch-inline-blobs.ts index f2799e500f6ae..d3bdf52097e57 100644 --- a/packages/next/server/web/sandbox/fetch-inline-blobs.ts +++ b/packages/next/server/web/sandbox/fetch-inline-blobs.ts @@ -11,7 +11,7 @@ import { requestToBodyStream } from '../../body-streams' export async function fetchInlineBlob(options: { input: RequestInfo distDir: string - assets: EdgeFunctionDefinition['assets'] + assets: EdgeFunctionDefinition['blobs'] context: { Response: any } }): Promise { const inputString = String(options.input) diff --git a/packages/next/server/web/sandbox/sandbox.ts b/packages/next/server/web/sandbox/sandbox.ts index 0390a8f12275d..9b0c5adeafee9 100644 --- a/packages/next/server/web/sandbox/sandbox.ts +++ b/packages/next/server/web/sandbox/sandbox.ts @@ -12,7 +12,7 @@ type RunnerFn = (params: { paths: string[] request: RequestData useCache: boolean - edgeFunctionEntry: Pick + edgeFunctionEntry: Pick distDir: string }) => Promise diff --git a/test/e2e/middleware-general/test/index.test.ts b/test/e2e/middleware-general/test/index.test.ts index 1717eaa8c5ec3..52018cdf45478 100644 --- a/test/e2e/middleware-general/test/index.test.ts +++ b/test/e2e/middleware-general/test/index.test.ts @@ -114,7 +114,7 @@ describe('Middleware Runtime', () => { page: '/', regexp: '^/.*$', wasm: [], - assets: [], + blobs: [], }, }) }) diff --git a/test/e2e/middleware-trailing-slash/test/index.test.ts b/test/e2e/middleware-trailing-slash/test/index.test.ts index 4277ec9ae80f7..8a1ad9c5947cf 100644 --- a/test/e2e/middleware-trailing-slash/test/index.test.ts +++ b/test/e2e/middleware-trailing-slash/test/index.test.ts @@ -61,7 +61,7 @@ describe('Middleware Runtime trailing slash', () => { page: '/', regexp: '^/.*$', wasm: [], - assets: [], + blobs: [], }, }) }) From 668d764074fdced41538bac4eb393e3fbf6a74b4 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Tue, 12 Jul 2022 12:53:07 +0300 Subject: [PATCH 09/16] rename blobs to assets and use webpack's hashing algo --- .../webpack/loaders/get-module-build-info.ts | 2 +- .../loaders/next-middleware-asset-loader.ts | 13 +++++---- .../webpack/plugins/middleware-plugin.ts | 12 ++++----- packages/next/server/next-server.ts | 10 +++---- packages/next/server/web/sandbox/context.ts | 12 ++++----- ...inline-blobs.ts => fetch-inline-assets.ts} | 12 ++++----- packages/next/server/web/sandbox/sandbox.ts | 2 +- .../index.test.ts | 27 ++++++++++++++++--- .../e2e/middleware-general/test/index.test.ts | 2 +- .../test/index.test.ts | 2 +- 10 files changed, 57 insertions(+), 37 deletions(-) rename packages/next/server/web/sandbox/{fetch-inline-blobs.ts => fetch-inline-assets.ts} (69%) diff --git a/packages/next/build/webpack/loaders/get-module-build-info.ts b/packages/next/build/webpack/loaders/get-module-build-info.ts index 022292225273c..e96894a278930 100644 --- a/packages/next/build/webpack/loaders/get-module-build-info.ts +++ b/packages/next/build/webpack/loaders/get-module-build-info.ts @@ -11,7 +11,7 @@ export function getModuleBuildInfo(webpackModule: webpack5.Module) { nextEdgeSSR?: EdgeSSRMeta nextUsedEnvVars?: Set nextWasmMiddlewareBinding?: AssetBinding - nextBlobMiddlewareBinding?: AssetBinding + nextAssetMiddlewareBinding?: AssetBinding usingIndirectEval?: boolean | Set route?: RouteMeta importLocByPath?: Map diff --git a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts index dc1b37d0b2f0b..4cb0bbd83435b 100644 --- a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts +++ b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts @@ -1,11 +1,14 @@ +import loaderUtils from 'next/dist/compiled/loader-utils3' import { getModuleBuildInfo } from './get-module-build-info' -import crypto from 'crypto' export default function MiddlewareAssetLoader(this: any, source: Buffer) { - const name = sha1(source) + const name = loaderUtils.interpolateName(this, '[name].[hash:8].[ext]', { + context: this.rootContext, + content: source, + }) const filePath = `edge-chunks/asset_${name}` const buildInfo = getModuleBuildInfo(this._module) - buildInfo.nextBlobMiddlewareBinding = { + buildInfo.nextAssetMiddlewareBinding = { filePath: `server/${filePath}`, name, } @@ -14,7 +17,3 @@ export default function MiddlewareAssetLoader(this: any, source: Buffer) { } export const raw = true - -function sha1(source: string | Buffer) { - return crypto.createHash('sha1').update(source).digest('hex') -} diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index c1db74f148835..8bac17d8f8136 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -24,7 +24,7 @@ export interface EdgeFunctionDefinition { page: string regexp: string wasm?: AssetBinding[] - blobs?: AssetBinding[] + assets?: AssetBinding[] } export interface MiddlewareManifest { @@ -40,7 +40,7 @@ interface EntryMetadata { edgeSSR?: EdgeSSRMeta env: Set wasmBindings: Set - blobBindings: Set + assetBindings: Set } const NAME = 'MiddlewarePlugin' @@ -416,7 +416,7 @@ function getExtractMetadata(params: { const entryMetadata: EntryMetadata = { env: new Set(), wasmBindings: new Set(), - blobBindings: new Set(), + assetBindings: new Set(), } for (const entryModule of entryModules) { @@ -488,8 +488,8 @@ function getExtractMetadata(params: { entryMetadata.wasmBindings.add(buildInfo.nextWasmMiddlewareBinding) } - if (buildInfo?.nextBlobMiddlewareBinding) { - entryMetadata.blobBindings.add(buildInfo.nextBlobMiddlewareBinding) + if (buildInfo?.nextAssetMiddlewareBinding) { + entryMetadata.assetBindings.add(buildInfo.nextAssetMiddlewareBinding) } /** @@ -568,7 +568,7 @@ function getCreateAssets(params: { page: page, regexp, wasm: Array.from(metadata.wasmBindings), - blobs: Array.from(metadata.blobBindings), + assets: Array.from(metadata.assetBindings), } if (metadata.edgeApiFunction || metadata.edgeSSR) { diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 9c890b73e0a2b..6af495b456188 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -610,7 +610,7 @@ export default class NextNodeServer extends BaseServer { ): Promise { // Due to the way we pass data by mutating `renderOpts`, we can't extend the // object here but only updating its `serverComponentManifest` field. - // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 + // https://github.com/vercel/next.js/asset/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 renderOpts.serverComponentManifest = this.serverComponentManifest if ( @@ -927,10 +927,10 @@ export default class NextNodeServer extends BaseServer { protected isServeableUrl(untrustedFileUrl: string): boolean { // This method mimics what the version of `send` we use does: // 1. decodeURIComponent: - // https://github.com/pillarjs/send/blob/0.17.1/index.js#L989 - // https://github.com/pillarjs/send/blob/0.17.1/index.js#L518-L522 + // https://github.com/pillarjs/send/asset/0.17.1/index.js#L989 + // https://github.com/pillarjs/send/asset/0.17.1/index.js#L518-L522 // 2. resolve: - // https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561 + // https://github.com/pillarjs/send/asset/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561 let decodedUntrustedFilePath: string try { @@ -1121,7 +1121,7 @@ export default class NextNodeServer extends BaseServer { ...binding, filePath: join(this.distDir, binding.filePath), })), - blobs: (pageInfo.blobs ?? []).map((binding) => { + assets: (pageInfo.assets ?? []).map((binding) => { return { ...binding, filePath: join(this.distDir, binding.filePath), diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index 8f8ac2fda5a01..23f663b270b2d 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -9,7 +9,7 @@ import { EdgeRuntime } from 'next/dist/compiled/edge-runtime' import { readFileSync, promises as fs } from 'fs' import { validateURL } from '../utils' import { pick } from '../../../lib/pick' -import { fetchInlineBlob } from './fetch-inline-blobs' +import { fetchInlineAsset } from './fetch-inline-assets' import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin' const WEBPACK_HASH_REGEX = @@ -51,7 +51,7 @@ interface ModuleContextOptions { useCache: boolean env: string[] distDir: string - edgeFunctionEntry: Pick + edgeFunctionEntry: Pick } const pendingModuleCaches = new Map>() @@ -201,14 +201,14 @@ Learn More: https://nextjs.org/docs/messages/middleware-dynamic-wasm-compilation const __fetch = context.fetch context.fetch = async (input: RequestInfo, init: RequestInit = {}) => { - const blobResponse = await fetchInlineBlob({ + const assetResponse = await fetchInlineAsset({ input, - assets: options.edgeFunctionEntry.blobs, + assets: options.edgeFunctionEntry.assets, distDir: options.distDir, context, }) - if (blobResponse) { - return blobResponse + if (assetResponse) { + return assetResponse } init.headers = new Headers(init.headers ?? {}) diff --git a/packages/next/server/web/sandbox/fetch-inline-blobs.ts b/packages/next/server/web/sandbox/fetch-inline-assets.ts similarity index 69% rename from packages/next/server/web/sandbox/fetch-inline-blobs.ts rename to packages/next/server/web/sandbox/fetch-inline-assets.ts index d3bdf52097e57..966d7f72c8cc6 100644 --- a/packages/next/server/web/sandbox/fetch-inline-blobs.ts +++ b/packages/next/server/web/sandbox/fetch-inline-assets.ts @@ -5,13 +5,13 @@ import { requestToBodyStream } from '../../body-streams' /** * Short-circuits the `fetch` function - * to return a stream for a given blob, if a user used `new URL("file", import.meta.url)`. - * This allows to embed blobs in Edge Runtime. + * to return a stream for a given asset, if a user used `new URL("file", import.meta.url)`. + * This allows to embed assets in Edge Runtime. */ -export async function fetchInlineBlob(options: { +export async function fetchInlineAsset(options: { input: RequestInfo distDir: string - assets: EdgeFunctionDefinition['blobs'] + assets: EdgeFunctionDefinition['assets'] context: { Response: any } }): Promise { const inputString = String(options.input) @@ -33,7 +33,7 @@ export async function fetchInlineBlob(options: { ) if (fileIsReadable) { - const blob = createReadStream(filePath) - return new options.context.Response(requestToBodyStream(blob)) + const readStream = createReadStream(filePath) + return new options.context.Response(requestToBodyStream(readStream)) } } diff --git a/packages/next/server/web/sandbox/sandbox.ts b/packages/next/server/web/sandbox/sandbox.ts index 9b0c5adeafee9..0390a8f12275d 100644 --- a/packages/next/server/web/sandbox/sandbox.ts +++ b/packages/next/server/web/sandbox/sandbox.ts @@ -12,7 +12,7 @@ type RunnerFn = (params: { paths: string[] request: RequestData useCache: boolean - edgeFunctionEntry: Pick + edgeFunctionEntry: Pick distDir: string }) => Promise diff --git a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts index d3461c873a880..a5def3cad171d 100644 --- a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts +++ b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts @@ -3,8 +3,9 @@ import { NextInstance } from 'test/lib/next-modes/base' import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils' import path from 'path' import { promises as fs } from 'fs' +import { readJson } from 'fs-extra' -describe('Edge Compiler can import blob assets', () => { +describe('Edge Compiler can import asset assets', () => { let next: NextInstance beforeAll(async () => { @@ -15,14 +16,14 @@ describe('Edge Compiler can import blob assets', () => { }) afterAll(() => next.destroy()) - it('allows to fetch text blobs', async () => { + it('allows to fetch text assets', async () => { const html = await renderViaHTTP(next.url, '/api/edge', { handler: 'text-file', }) expect(html).toContain('Hello, from text-file.txt!') }) - it('allows to fetch image blobs', async () => { + it('allows to fetch image assets', async () => { const response = await fetchViaHTTP(next.url, '/api/edge', { handler: 'image-file', }) @@ -32,4 +33,24 @@ describe('Edge Compiler can import blob assets', () => { ) expect(buffer.equals(image)).toBeTrue() }) + + it('extracts all the assets from the bundle', async () => { + const manifestPath = path.join( + next.testDir, + '.next/server/middleware-manifest.json' + ) + const manifest = await readJson(manifestPath) + expect(manifest.functions['/api/edge'].assets).toMatchObject([ + { + name: expect.stringMatching(/^text-file\.[0-9a-f]{8}\.txt$/), + filePath: expect.stringMatching( + /^server\/edge-chunks\/asset_text-file/ + ), + }, + { + name: expect.stringMatching(/^vercel\.[0-9a-f]{8}\.png$/), + filePath: expect.stringMatching(/^server\/edge-chunks\/asset_vercel/), + }, + ]) + }) }) diff --git a/test/e2e/middleware-general/test/index.test.ts b/test/e2e/middleware-general/test/index.test.ts index 52018cdf45478..1717eaa8c5ec3 100644 --- a/test/e2e/middleware-general/test/index.test.ts +++ b/test/e2e/middleware-general/test/index.test.ts @@ -114,7 +114,7 @@ describe('Middleware Runtime', () => { page: '/', regexp: '^/.*$', wasm: [], - blobs: [], + assets: [], }, }) }) diff --git a/test/e2e/middleware-trailing-slash/test/index.test.ts b/test/e2e/middleware-trailing-slash/test/index.test.ts index 8a1ad9c5947cf..4277ec9ae80f7 100644 --- a/test/e2e/middleware-trailing-slash/test/index.test.ts +++ b/test/e2e/middleware-trailing-slash/test/index.test.ts @@ -61,7 +61,7 @@ describe('Middleware Runtime trailing slash', () => { page: '/', regexp: '^/.*$', wasm: [], - blobs: [], + assets: [], }, }) }) From 9be0db265ca8050dd59d14491806f55029c87c74 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Tue, 12 Jul 2022 12:57:51 +0300 Subject: [PATCH 10/16] Dedupe correctly --- .../loaders/next-middleware-asset-loader.ts | 2 +- .../webpack/plugins/middleware-plugin.ts | 28 +++++++++++++------ .../index.test.ts | 4 +-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts index 4cb0bbd83435b..dc2e9c1f8f076 100644 --- a/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts +++ b/packages/next/build/webpack/loaders/next-middleware-asset-loader.ts @@ -2,7 +2,7 @@ import loaderUtils from 'next/dist/compiled/loader-utils3' import { getModuleBuildInfo } from './get-module-build-info' export default function MiddlewareAssetLoader(this: any, source: Buffer) { - const name = loaderUtils.interpolateName(this, '[name].[hash:8].[ext]', { + const name = loaderUtils.interpolateName(this, '[name].[hash].[ext]', { context: this.rootContext, content: source, }) diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 8bac17d8f8136..56f672f019230 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -39,8 +39,8 @@ interface EntryMetadata { edgeApiFunction?: EdgeMiddlewareMeta edgeSSR?: EdgeSSRMeta env: Set - wasmBindings: Set - assetBindings: Set + wasmBindings: Map + assetBindings: Map } const NAME = 'MiddlewarePlugin' @@ -415,8 +415,8 @@ function getExtractMetadata(params: { const entryMetadata: EntryMetadata = { env: new Set(), - wasmBindings: new Set(), - assetBindings: new Set(), + wasmBindings: new Map(), + assetBindings: new Map(), } for (const entryModule of entryModules) { @@ -485,11 +485,17 @@ function getExtractMetadata(params: { * append it to the entry wasm bindings. */ if (buildInfo?.nextWasmMiddlewareBinding) { - entryMetadata.wasmBindings.add(buildInfo.nextWasmMiddlewareBinding) + entryMetadata.wasmBindings.set( + buildInfo.nextWasmMiddlewareBinding.name, + buildInfo.nextWasmMiddlewareBinding.filePath + ) } if (buildInfo?.nextAssetMiddlewareBinding) { - entryMetadata.assetBindings.add(buildInfo.nextAssetMiddlewareBinding) + entryMetadata.assetBindings.set( + buildInfo.nextAssetMiddlewareBinding.name, + buildInfo.nextAssetMiddlewareBinding.filePath + ) } /** @@ -567,8 +573,14 @@ function getCreateAssets(params: { name: entrypoint.name, page: page, regexp, - wasm: Array.from(metadata.wasmBindings), - assets: Array.from(metadata.assetBindings), + wasm: Array.from(metadata.wasmBindings, ([name, filePath]) => ({ + name, + filePath, + })), + assets: Array.from(metadata.assetBindings, ([name, filePath]) => ({ + name, + filePath, + })), } if (metadata.edgeApiFunction || metadata.edgeSSR) { diff --git a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts index a5def3cad171d..60b15520f4d97 100644 --- a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts +++ b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts @@ -42,13 +42,13 @@ describe('Edge Compiler can import asset assets', () => { const manifest = await readJson(manifestPath) expect(manifest.functions['/api/edge'].assets).toMatchObject([ { - name: expect.stringMatching(/^text-file\.[0-9a-f]{8}\.txt$/), + name: expect.stringMatching(/^text-file\.[0-9a-f]{16}\.txt$/), filePath: expect.stringMatching( /^server\/edge-chunks\/asset_text-file/ ), }, { - name: expect.stringMatching(/^vercel\.[0-9a-f]{8}\.png$/), + name: expect.stringMatching(/^vercel\.[0-9a-f]{16}\.png$/), filePath: expect.stringMatching(/^server\/edge-chunks\/asset_vercel/), }, ]) From 372196d100a8987137cd7f53ad31b142b87addb1 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Tue, 12 Jul 2022 13:55:29 +0300 Subject: [PATCH 11/16] Add a Node.js dep test --- .../app/node_modules/my-pkg/hello/world.json | 1 + .../app/pages/api/edge.js | 7 +++++ .../index.test.ts | 26 ++++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 test/e2e/edge-compiler-can-import-blob-assets/app/node_modules/my-pkg/hello/world.json diff --git a/test/e2e/edge-compiler-can-import-blob-assets/app/node_modules/my-pkg/hello/world.json b/test/e2e/edge-compiler-can-import-blob-assets/app/node_modules/my-pkg/hello/world.json new file mode 100644 index 0000000000000..394d6c8bc1df3 --- /dev/null +++ b/test/e2e/edge-compiler-can-import-blob-assets/app/node_modules/my-pkg/hello/world.json @@ -0,0 +1 @@ +{ "i am": "a node dependency" } diff --git a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js index d4e8133f1df61..c667c2864a77b 100644 --- a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js +++ b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js @@ -27,6 +27,13 @@ const handlers = new Map([ return fetch(url) }, ], + [ + 'from-node-module', + async () => { + const url = new URL('my-pkg/hello/world.json', import.meta.url) + return fetch(url) + }, + ], ]) const defaultHandler = async () => diff --git a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts index 60b15520f4d97..67f0f77062d85 100644 --- a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts +++ b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts @@ -4,6 +4,7 @@ import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils' import path from 'path' import { promises as fs } from 'fs' import { readJson } from 'fs-extra' +import type { MiddlewareManifest } from 'next/build/webpack/plugins/middleware-plugin' describe('Edge Compiler can import asset assets', () => { let next: NextInstance @@ -11,7 +12,6 @@ describe('Edge Compiler can import asset assets', () => { beforeAll(async () => { next = await createNext({ files: new FileRef(path.join(__dirname, './app')), - dependencies: {}, }) }) afterAll(() => next.destroy()) @@ -34,13 +34,29 @@ describe('Edge Compiler can import asset assets', () => { expect(buffer.equals(image)).toBeTrue() }) + it('allows to assets from node_modules', async () => { + const response = await fetchViaHTTP(next.url, '/api/edge', { + handler: 'from-node-module', + }) + const json = await response.json() + expect(json).toEqual({ + 'i am': 'a node dependency', + }) + }) + it('extracts all the assets from the bundle', async () => { const manifestPath = path.join( next.testDir, '.next/server/middleware-manifest.json' ) - const manifest = await readJson(manifestPath) - expect(manifest.functions['/api/edge'].assets).toMatchObject([ + const manifest: MiddlewareManifest = await readJson(manifestPath) + const orderedAssets = manifest.functions['/api/edge'].assets.sort( + (a, z) => { + return String(a.name).localeCompare(z.name) + } + ) + + expect(orderedAssets).toMatchObject([ { name: expect.stringMatching(/^text-file\.[0-9a-f]{16}\.txt$/), filePath: expect.stringMatching( @@ -51,6 +67,10 @@ describe('Edge Compiler can import asset assets', () => { name: expect.stringMatching(/^vercel\.[0-9a-f]{16}\.png$/), filePath: expect.stringMatching(/^server\/edge-chunks\/asset_vercel/), }, + { + name: expect.stringMatching(/^world\.[0-9a-f]{16}\.json/), + filePath: expect.stringMatching(/^server\/edge-chunks\/asset_world/), + }, ]) }) }) From 6dba223926a29a489ad35cc2ac634b810b93a8e6 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Tue, 19 Jul 2022 14:14:14 +0300 Subject: [PATCH 12/16] Update packages/next/server/next-server.ts Co-authored-by: Tobias Koppers --- packages/next/server/next-server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 6af495b456188..d9f8e2394b2cb 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -610,7 +610,7 @@ export default class NextNodeServer extends BaseServer { ): Promise { // Due to the way we pass data by mutating `renderOpts`, we can't extend the // object here but only updating its `serverComponentManifest` field. - // https://github.com/vercel/next.js/asset/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 + // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 renderOpts.serverComponentManifest = this.serverComponentManifest if ( From 72e3a7f49486ff5ea29814268f819514da48db2b Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Tue, 19 Jul 2022 15:47:47 +0300 Subject: [PATCH 13/16] [code review] test remote URL fetches --- .../app/pages/api/edge.js | 20 ++++++++++++++++++ .../index.test.ts | 21 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js index c667c2864a77b..dd24197003cd8 100644 --- a/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js +++ b/test/e2e/edge-compiler-can-import-blob-assets/app/pages/api/edge.js @@ -34,6 +34,26 @@ const handlers = new Map([ return fetch(url) }, ], + [ + 'remote-full', + async () => { + const url = new URL('https://example.vercel.sh') + const response = await fetch(url) + const headers = new Headers(response.headers) + headers.delete('content-encoding') + return new Response(response.body, { headers, status: response.status }) + }, + ], + [ + 'remote-with-base', + async () => { + const url = new URL('/', 'https://example.vercel.sh') + const response = await fetch(url) + const headers = new Headers(response.headers) + headers.delete('content-encoding') + return new Response(response.body, { headers, status: response.status }) + }, + ], ]) const defaultHandler = async () => diff --git a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts index 67f0f77062d85..2e9668d185e0c 100644 --- a/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts +++ b/test/e2e/edge-compiler-can-import-blob-assets/index.test.ts @@ -16,6 +16,27 @@ describe('Edge Compiler can import asset assets', () => { }) afterAll(() => next.destroy()) + it('allows to fetch a remote URL', async () => { + const response = await fetchViaHTTP(next.url, '/api/edge', { + handler: 'remote-full', + }) + expect(await response.text()).toContain('Example Domain') + }) + + it('allows to fetch a remote URL with a path and basename', async () => { + const response = await fetchViaHTTP( + next.url, + '/api/edge', + { + handler: 'remote-with-base', + }, + { + compress: true, + } + ) + expect(await response.text()).toContain('Example Domain') + }) + it('allows to fetch text assets', async () => { const html = await renderViaHTTP(next.url, '/api/edge', { handler: 'text-file', From 5c861555cf9c1d725aac9b6b2551fb633fa15e99 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Tue, 19 Jul 2022 15:48:22 +0300 Subject: [PATCH 14/16] [code review] use `import type` for type-only imports --- packages/next/server/web/sandbox/fetch-inline-assets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/server/web/sandbox/fetch-inline-assets.ts b/packages/next/server/web/sandbox/fetch-inline-assets.ts index 966d7f72c8cc6..941ebd67b1670 100644 --- a/packages/next/server/web/sandbox/fetch-inline-assets.ts +++ b/packages/next/server/web/sandbox/fetch-inline-assets.ts @@ -1,7 +1,7 @@ import { createReadStream, promises as fs } from 'fs' import path from 'path' -import { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin' import { requestToBodyStream } from '../../body-streams' +import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin' /** * Short-circuits the `fetch` function From fb33685e46e0a8b877a613b87763c6f7d29f6cf2 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Tue, 19 Jul 2022 16:25:45 +0300 Subject: [PATCH 15/16] Update packages/next/server/next-server.ts Co-authored-by: Tobias Koppers --- packages/next/server/next-server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index d9f8e2394b2cb..200fd6c65d2d7 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -927,7 +927,7 @@ export default class NextNodeServer extends BaseServer { protected isServeableUrl(untrustedFileUrl: string): boolean { // This method mimics what the version of `send` we use does: // 1. decodeURIComponent: - // https://github.com/pillarjs/send/asset/0.17.1/index.js#L989 + // https://github.com/pillarjs/send/blob/0.17.1/index.js#L989 // https://github.com/pillarjs/send/asset/0.17.1/index.js#L518-L522 // 2. resolve: // https://github.com/pillarjs/send/asset/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561 From 8b8f70d3861134c054582eddfa1ca887868ea673 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Tue, 19 Jul 2022 18:20:54 +0300 Subject: [PATCH 16/16] Apply suggestions from code review Co-authored-by: JJ Kasper --- packages/next/server/next-server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 200fd6c65d2d7..f3e4ec22c14b2 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -928,9 +928,9 @@ export default class NextNodeServer extends BaseServer { // This method mimics what the version of `send` we use does: // 1. decodeURIComponent: // https://github.com/pillarjs/send/blob/0.17.1/index.js#L989 - // https://github.com/pillarjs/send/asset/0.17.1/index.js#L518-L522 + // https://github.com/pillarjs/send/blob/0.17.1/index.js#L518-L522 // 2. resolve: - // https://github.com/pillarjs/send/asset/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561 + // https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561 let decodedUntrustedFilePath: string try {