Compare commits

..

5 Commits

Author SHA1 Message Date
JJ Kasper
1b211f28df Publish Stable
- vercel@28.4.17
 - @vercel/client@12.2.17
 - @vercel/frameworks@1.1.12
 - @vercel/fs-detectors@3.4.9
 - @vercel/next@3.2.11
 - @vercel/redwood@1.0.35
 - @vercel/routing-utils@2.1.3
 - @vercel/static-build@1.0.36
2022-11-08 16:21:25 -08:00
JJ Kasper
c2d0887b94 [next][routing-utils] Add missing matcher support (#8874)
### Related Issues

This adds handling to ensure we pass through the `missing` route field
correctly for custom routes and middleware matchers. Tests are also
added in the complimentary Next.js PR for this, example deployment can
be seen here
https://vtest314-e2e-tests-jj4-vtest314-next-e2e-tests.vercel.app/

x-ref: [slack
thread](https://vercel.slack.com/archives/C03S8ED1DKM/p1667935428788529?thread_ts=1667850697.542269&cid=C03S8ED1DKM)
x-ref: https://github.com/vercel/next.js/pull/42660

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-08 16:17:34 -08:00
JJ Kasper
fbb8bba4cf Publish Stable
- vercel@28.4.16
 - @vercel/next@3.2.10
2022-11-08 11:03:01 -08:00
JJ Kasper
56cc87fe9d [next] Handle skip normalize flag for middleware (#8873)
### Related Issues

x-ref: [slack
thread](https://vercel.slack.com/archives/C01224Q5M99/p1667927545637489?thread_ts=1664536480.045539&cid=C01224Q5M99)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-08 10:31:02 -08:00
JJ Kasper
0027ffa65b [next] Ensure rsc dynamic routes are correct (#8866)
### Related Issues

This ensures we properly route dynamic routes with segments in the
middle of the path for rsc paths and also ensures we normalize the
header values from the routes-manifest correctly.

x-ref: https://github.com/vercel/next.js/issues/42364
x-ref:
https://github.com/vercel/next.js/actions/runs/3415662540/jobs/5685787894

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-08 07:24:32 -08:00
20 changed files with 298 additions and 52 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.4.15",
"version": "28.4.17",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -44,13 +44,13 @@
"@vercel/build-utils": "5.5.7",
"@vercel/go": "2.2.15",
"@vercel/hydrogen": "0.0.29",
"@vercel/next": "3.2.9",
"@vercel/next": "3.2.11",
"@vercel/node": "2.6.2",
"@vercel/python": "3.1.24",
"@vercel/redwood": "1.0.34",
"@vercel/redwood": "1.0.35",
"@vercel/remix": "1.0.35",
"@vercel/ruby": "1.3.41",
"@vercel/static-build": "1.0.35",
"@vercel/static-build": "1.0.36",
"update-notifier": "5.1.0"
},
"devDependencies": {
@@ -95,10 +95,10 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.2.16",
"@vercel/client": "12.2.17",
"@vercel/error-utils": "1.0.3",
"@vercel/frameworks": "1.1.11",
"@vercel/fs-detectors": "3.4.8",
"@vercel/frameworks": "1.1.12",
"@vercel/fs-detectors": "3.4.9",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@zeit/source-map-support": "0.6.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.2.16",
"version": "12.2.17",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -44,7 +44,7 @@
},
"dependencies": {
"@vercel/build-utils": "5.5.7",
"@vercel/routing-utils": "2.1.2",
"@vercel/routing-utils": "2.1.3",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "1.1.11",
"version": "1.1.12",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [
@@ -21,7 +21,7 @@
"@types/js-yaml": "3.12.1",
"@types/node": "14.18.33",
"@types/node-fetch": "2.5.8",
"@vercel/routing-utils": "2.1.2",
"@vercel/routing-utils": "2.1.3",
"ajv": "6.12.2",
"typescript": "4.3.4"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.4.8",
"version": "3.4.9",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -19,8 +19,8 @@
"test-unit": "yarn test"
},
"dependencies": {
"@vercel/frameworks": "1.1.11",
"@vercel/routing-utils": "2.1.2",
"@vercel/frameworks": "1.1.12",
"@vercel/routing-utils": "2.1.3",
"glob": "8.0.3",
"js-yaml": "4.1.0",
"json5": "2.2.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.2.9",
"version": "3.2.11",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -46,7 +46,7 @@
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "5.5.7",
"@vercel/nft": "0.22.1",
"@vercel/routing-utils": "2.1.2",
"@vercel/routing-utils": "2.1.3",
"async-sema": "3.0.1",
"buffer-crc32": "0.2.13",
"cheerio": "1.0.0-rc.10",

View File

@@ -1137,7 +1137,24 @@ export async function serverBuild({
}
}
const rscHeader = routesManifest.rsc?.header || '__rsc__';
const rscHeader = routesManifest.rsc?.header?.toLowerCase() || '__rsc__';
const completeDynamicRoutes: typeof dynamicRoutes = [];
if (appDir) {
for (const route of dynamicRoutes) {
completeDynamicRoutes.push(route);
completeDynamicRoutes.push({
...route,
src: route.src.replace(
new RegExp(escapeStringRegexp('(?:/)?$')),
'(?:\\.rsc)?(?:/)?$'
),
dest: route.dest?.replace(/($|\?)/, '.rsc$1'),
});
}
} else {
completeDynamicRoutes.push(...dynamicRoutes);
}
return {
wildcard: wildcardConfig,
@@ -1313,8 +1330,16 @@ export async function serverBuild({
// middleware comes directly after redirects but before
// beforeFiles rewrites as middleware is not a "file" route
...(routesManifest?.skipMiddlewareUrlNormalize
? denormalizeNextDataRoute(true)
: []),
...(isCorrectMiddlewareOrder ? middleware.staticRoutes : []),
...(routesManifest?.skipMiddlewareUrlNormalize
? normalizeNextDataRoute(true)
: []),
...beforeFilesRewrites,
// Make sure to 404 for the /404 path itself
@@ -1504,7 +1529,7 @@ export async function serverBuild({
? // when resolving data routes for middleware we need to include
// all dynamic routes including non-SSG/SSP so that the priority
// is correct
dynamicRoutes
completeDynamicRoutes
.map(route => {
route = Object.assign({}, route);
let normalizedSrc = route.src;
@@ -1579,7 +1604,7 @@ export async function serverBuild({
// Dynamic routes (must come after dataRoutes as dataRoutes are more
// specific)
...dynamicRoutes,
...completeDynamicRoutes,
...(isNextDataServerResolving
? [

View File

@@ -213,6 +213,7 @@ type RoutesManifestOld = {
header: string;
varyHeader: string;
};
skipMiddlewareUrlNormalize?: boolean;
};
type RoutesManifestV4 = Omit<RoutesManifestOld, 'dynamicRoutes' | 'version'> & {
@@ -2277,6 +2278,7 @@ interface EdgeFunctionInfoV2 extends BaseEdgeFunctionInfo {
interface EdgeFunctionMatcher {
regexp: string;
has?: HasField;
missing?: HasField;
}
export async function getMiddlewareBundle({
@@ -2478,6 +2480,7 @@ export async function getMiddlewareBundle({
key: 'x-prerender-revalidate',
value: prerenderBypassToken,
},
...(matcher.missing || []),
],
};
@@ -2608,6 +2611,9 @@ function getRouteMatchers(
if (matcher.has) {
m.has = normalizeHas(matcher.has);
}
if (matcher.missing) {
m.missing = normalizeHas(matcher.missing);
}
return m;
});
}

View File

@@ -2,8 +2,5 @@ module.exports = {
experimental: {
appDir: true,
runtime: 'experimental-edge',
serverComponents: true,
legacyBrowsers: false,
browsersListForSwc: true,
},
};

View File

@@ -15,7 +15,7 @@
"path": "/edge",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
@@ -24,7 +24,7 @@
"path": "/edge",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream"
@@ -39,7 +39,7 @@
"path": "/",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
@@ -48,7 +48,7 @@
"path": "/",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream"

View File

@@ -2,9 +2,6 @@ module.exports = {
experimental: {
appDir: true,
runtime: 'nodejs',
serverComponents: true,
legacyBrowsers: false,
browsersListForSwc: true,
},
rewrites: async () => {
return [

View File

@@ -11,14 +11,14 @@
"status": 200,
"mustContain": "hello from app/dashboard",
"responseHeaders": {
"vary": "__rsc__, __next_router_state_tree__, __next_router_prefetch__"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
@@ -27,11 +27,11 @@
"path": "/dashboard",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream",
"vary": "__rsc__, __next_router_state_tree__, __next_router_prefetch__"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
@@ -63,7 +63,7 @@
"path": "/",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"

View File

@@ -0,0 +1,10 @@
export default function DeploymentsPage(props) {
return (
<>
<p>
hello from app/dashboard/deployments/[id]/settings. ID is:{' '}
{props.params.id}
</p>
</>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>catchall</p>;
}

View File

@@ -2,9 +2,6 @@ module.exports = {
experimental: {
appDir: true,
runtime: 'nodejs',
serverComponents: true,
legacyBrowsers: false,
browsersListForSwc: true,
},
rewrites: async () => {
return [

View File

@@ -6,19 +6,59 @@
}
],
"probes": [
{
"path": "/dashboard/deployments/123/settings",
"status": 200,
"mustContain": "hello from app/dashboard/deployments/[id]/settings",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/deployments/123/settings",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
},
{
"path": "/dashboard/deployments/catchall/something",
"status": 200,
"mustContain": "catchall",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/deployments/catchall/something",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
},
{
"path": "/dashboard",
"status": 200,
"mustContain": "hello from app/dashboard",
"responseHeaders": {
"vary": "__rsc__, __next_router_state_tree__, __next_router_prefetch__"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
@@ -27,11 +67,11 @@
"path": "/dashboard",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream",
"vary": "__rsc__, __next_router_state_tree__, __next_router_prefetch__"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
@@ -68,7 +108,7 @@
"path": "/",
"status": 200,
"headers": {
"__rsc__": "1"
"RSC": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/redwood",
"version": "1.0.34",
"version": "1.0.35",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs",
@@ -20,7 +20,7 @@
},
"dependencies": {
"@vercel/nft": "0.22.1",
"@vercel/routing-utils": "2.1.2",
"@vercel/routing-utils": "2.1.3",
"semver": "6.1.1"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/routing-utils",
"version": "2.1.2",
"version": "2.1.3",
"description": "Vercel routing utilities",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@@ -51,6 +51,9 @@ export function convertRedirects(
return redirects.map(r => {
const { src, segments } = sourceToRegex(r.source);
const hasSegments = collectHasSegments(r.has);
normalizeHasKeys(r.has);
normalizeHasKeys(r.missing);
try {
const loc = replaceSegments(segments, hasSegments, r.destination, true);
let status: number;
@@ -70,6 +73,9 @@ export function convertRedirects(
if (r.has) {
route.has = r.has;
}
if (r.missing) {
route.missing = r.missing;
}
return route;
} catch (e) {
throw new Error(`Failed to parse redirect: ${JSON.stringify(r)}`);
@@ -84,6 +90,9 @@ export function convertRewrites(
return rewrites.map(r => {
const { src, segments } = sourceToRegex(r.source);
const hasSegments = collectHasSegments(r.has);
normalizeHasKeys(r.has);
normalizeHasKeys(r.missing);
try {
const dest = replaceSegments(
segments,
@@ -97,6 +106,9 @@ export function convertRewrites(
if (r.has) {
route.has = r.has;
}
if (r.missing) {
route.missing = r.missing;
}
return route;
} catch (e) {
throw new Error(`Failed to parse rewrite: ${JSON.stringify(r)}`);
@@ -109,6 +121,9 @@ export function convertHeaders(headers: Header[]): Route[] {
const obj: { [key: string]: string } = {};
const { src, segments } = sourceToRegex(h.source);
const hasSegments = collectHasSegments(h.has);
normalizeHasKeys(h.has);
normalizeHasKeys(h.missing);
const namedSegments = segments.filter(name => name !== UN_NAMED_SEGMENT);
const indexes: { [k: string]: string } = {};
@@ -140,6 +155,9 @@ export function convertHeaders(headers: Header[]): Route[] {
if (h.has) {
route.has = h.has;
}
if (h.missing) {
route.missing = h.missing;
}
return route;
});
}
@@ -193,14 +211,19 @@ export function sourceToRegex(source: string): {
const namedGroupsRegex = /\(\?<([a-zA-Z][a-zA-Z0-9]*)>/g;
const normalizeHasKeys = (hasItems: HasField = []) => {
for (const hasItem of hasItems) {
if ('key' in hasItem && hasItem.type === 'header') {
hasItem.key = hasItem.key.toLowerCase();
}
}
return hasItems;
};
export function collectHasSegments(has?: HasField) {
const hasSegments = new Set<string>();
for (const hasItem of has || []) {
if ('key' in hasItem && hasItem.type === 'header') {
hasItem.key = hasItem.key.toLowerCase();
}
if (!hasItem.value && 'key' in hasItem) {
hasSegments.add(hasItem.key);
}

View File

@@ -224,6 +224,17 @@ test('convertRedirects', () => {
],
permanent: false,
},
{
source: '/hello/:first',
destination: '/another',
missing: [
{
type: 'host',
value: '(?<a>.*)\\.(?<b>.*)',
},
],
permanent: false,
},
{
source: '/hello/:first',
destination:
@@ -384,6 +395,19 @@ test('convertRedirects', () => {
src: '^\\/hello(?:\\/([^\\/]+?))$',
status: 307,
},
{
missing: [
{
type: 'host',
value: '(?<a>.*)\\.(?<b>.*)',
},
],
headers: {
Location: '/another',
},
src: '^\\/hello(?:\\/([^\\/]+?))$',
status: 307,
},
{
has: [
{
@@ -453,6 +477,7 @@ test('convertRedirects', () => {
['/hello/world', '/hello/again'],
['/hello/world'],
['/hello/world'],
['/hello/world'],
];
const mustNotMatch = [
@@ -474,6 +499,7 @@ test('convertRedirects', () => {
['/feature/first', '/feature'],
['/hello', '/hello/another/one'],
['/hellooo'],
['/hellooo'],
['/helloooo'],
];
@@ -586,6 +612,48 @@ test('convertRewrites', () => {
},
],
},
{
source: '/hello/:first',
destination: '/another',
missing: [
{
type: 'header',
key: 'x-rewrite',
},
{
type: 'cookie',
key: 'loggedIn',
value: '1',
},
{
type: 'host',
value: 'vercel.com',
},
{
type: 'host',
value: '(?<a>.*)\\.(?<b>.*)',
},
{
type: 'header',
key: 'host',
value: '(?<c>.*)\\.(?<d>.*)',
},
{
type: 'query',
key: 'username',
},
{
type: 'header',
key: 'x-pathname',
value: '(?<pathname>.*)',
},
{
type: 'header',
key: 'X-Pathname',
value: '(?<another>hello|world)',
},
],
},
{
source: '/array-query-string/:id/:name',
destination: 'https://example.com/?tag=1&tag=2',
@@ -740,6 +808,49 @@ test('convertRewrites', () => {
],
src: '^\\/hello(?:\\/([^\\/]+?))$',
},
{
check: true,
dest: '/another?first=$1',
missing: [
{
key: 'x-rewrite',
type: 'header',
},
{
key: 'loggedIn',
type: 'cookie',
value: '1',
},
{
type: 'host',
value: 'vercel.com',
},
{
type: 'host',
value: '(?<a>.*)\\.(?<b>.*)',
},
{
key: 'host',
type: 'header',
value: '(?<c>.*)\\.(?<d>.*)',
},
{
type: 'query',
key: 'username',
},
{
type: 'header',
key: 'x-pathname',
value: '(?<pathname>.*)',
},
{
type: 'header',
key: 'x-pathname',
value: '(?<another>hello|world)',
},
],
src: '^\\/hello(?:\\/([^\\/]+?))$',
},
{
src: '^\\/array-query-string(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))$',
dest: 'https://example.com/?tag=1&tag=2&id=$1&name=$2',
@@ -776,6 +887,7 @@ test('convertRewrites', () => {
['/hello/world', '/hello/again'],
['/hello/world'],
['/hello/world'],
['/hello/world'],
['/array-query-string/10/email'],
['/en/hello'],
];
@@ -802,6 +914,7 @@ test('convertRewrites', () => {
['/hello', '/hello/another/one'],
['/hllooo'],
['/hllooo'],
['/hllooo'],
['/array-query-string/10'],
['/en/hello/world', '/en/hello/'],
];
@@ -919,6 +1032,25 @@ test('convertHeaders', () => {
},
],
},
{
source: '/hello/:first',
missing: [
{
type: 'host',
value: '(?<a>.*)\\.(?<b>.*)',
},
],
headers: [
{
key: 'x-a',
value: 'a',
},
{
key: 'x-b',
value: 'b',
},
],
},
{
source: '/hello/:first',
has: [
@@ -1057,6 +1189,20 @@ test('convertHeaders', () => {
},
src: '^\\/hello(?:\\/([^\\/]+?))$',
},
{
continue: true,
missing: [
{
type: 'host',
value: '(?<a>.*)\\.(?<b>.*)',
},
],
headers: {
'x-a': 'a',
'x-b': 'b',
},
src: '^\\/hello(?:\\/([^\\/]+?))$',
},
{
continue: true,
has: [
@@ -1133,6 +1279,7 @@ test('convertHeaders', () => {
['/like/params/first', '/like/params/second'],
['/hello/world'],
['/hello/world'],
['/hello/world'],
['/hello'],
];
@@ -1143,6 +1290,7 @@ test('convertHeaders', () => {
['/non-match', '/like/params', '/like/params/'],
['/hellooo'],
['/hellooo'],
['/hellooo'],
[],
];

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "1.0.35",
"version": "1.0.36",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -37,9 +37,9 @@
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@vercel/build-utils": "5.5.7",
"@vercel/frameworks": "1.1.11",
"@vercel/frameworks": "1.1.12",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.1.2",
"@vercel/routing-utils": "2.1.3",
"@vercel/static-config": "2.0.6",
"fs-extra": "10.0.0",
"get-port": "5.0.0",