Skip to content

Commit

Permalink
Add initial vercel-plugin-middleware (#6892)
Browse files Browse the repository at this point in the history
* Add initial `vercel-plugin-middleware`

* Ignore `entries.js` from ESLint

* Add `runDevMiddleware()` stub

* Add test

* Add support for "_middleware.{js,ts}" to `vercel dev` (#6880)

* Add websandbox from next.js codebase.

* Use node-fetch instead of next's polyfilled fetch.

* Handle middleware rewrites.

* Add response, headers, and request to websandbox context.

* Move websandbox dependency to middleware plugin.

* Add integration tests, update websandbox to support ts files and json imports.

* commit yarn.lock changes after rebasing

* Clean up left over console.logs, fix some tsc issues, and rebase issue.

* Fix failing test and eslint.

* Fix middleware test on windows.

* [examples] Update Vercel Next.js example template to 12.0.1 (#6905)

* Mark the Plugins as external to CLI's ncc build

* [cli] Improve tracing in vc build (#6898)

* [cli] Fix tracing for `vc build`

* Ignore object when there are no changes

* Make Next < 12 work with FS API w/ nft

* Update packages/cli/src/commands/build.ts

Co-authored-by: Nathan Rajlich <n@n8.io>

* Document how Next.js processing works in build

* [cli] Fix static assets (#6906)

* Make sure output path is .next

* Fix up require-server-files for processing

* Fix typo

* Move static

* Update static rename

Co-authored-by: Andy Bitz <artzbitz@gmail.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Andy <AndyBitz@users.noreply.github.com>

* Publish Canary

 - vercel@23.1.3-canary.17
 - @vercel/client@10.2.3-canary.15
 - @vercel/static-config@0.0.1-canary.0

* [cli] Ignore `.env` and `.gitignore` in "vc build" (#6910)

* Publish Canary

 - vercel@23.1.3-canary.18

* Pass workPath to plugins.

The new plugin architecture doesn't pass a full BuildOptions object, previous
to this commit it wasn't passing any options at all. I've added workingPath to
support running dev/build from directories other than the project root.

* Remove error state when package.json exists, but no build script

This allows vercel build to continue working for projects that are not using
frameworks, but use package.json to manage dependencies.

* Fix types, pull in middleware header fix from next.js

Next js PR w/ the header fix:
vercel/next.js#30560

* Fix missing entries file for vc build.

* Update call signature of middleware when using vc build.

Co-authored-by: Drew Bredvick <dbredvick@gmail.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Jared Palmer <jared@jaredpalmer.com>
Co-authored-by: Andy Bitz <artzbitz@gmail.com>
Co-authored-by: Andy <AndyBitz@users.noreply.github.com>

Co-authored-by: Gary Borton <gdborton@gmail.com>
Co-authored-by: Drew Bredvick <dbredvick@gmail.com>
Co-authored-by: Jared Palmer <jared@jaredpalmer.com>
Co-authored-by: Andy Bitz <artzbitz@gmail.com>
Co-authored-by: Andy <AndyBitz@users.noreply.github.com>
  • Loading branch information
6 people committed Oct 30, 2021
1 parent df9a4af commit 44868d7
Show file tree
Hide file tree
Showing 40 changed files with 2,602 additions and 154 deletions.
6 changes: 2 additions & 4 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
node_modules
dist
examples
packages/build-utils/test/fixtures
packages/*/test/fixtures
packages/cli/@types
packages/cli/download
packages/cli/dist
packages/cli/test/fixtures
packages/cli/test/dev/fixtures
packages/cli/bin
packages/cli/link
packages/cli/src/util/dev/templates/*.ts
packages/client/tests/fixtures
packages/client/lib
packages/node/src/bridge.ts
packages/node/test/fixtures
packages/node-bridge/bridge.js
packages/node-bridge/launcher.js
packages/static-config/test/fixtures
packages/middleware/src/entries.js
11 changes: 10 additions & 1 deletion packages/cli/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,16 @@ async function main() {

// Do the initial `ncc` build
console.log();
const args = ['ncc', 'build', '--external', 'update-notifier'];
const args = [
'ncc',
'build',
'--external',
'update-notifier',
'--external',
'vercel-plugin-node',
'--external',
'vercel-plugin-middleware',
];
if (isDev) {
args.push('--source-map');
}
Expand Down
22 changes: 6 additions & 16 deletions packages/cli/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,10 @@ export default async function main(client: Client) {

// Clean the output directory
fs.removeSync(join(cwd, OUTPUT_DIR));
let result: boolean;

if (typeof buildState.buildCommand === 'string') {
client.output.log(`Running Build Command: ${cmd(buildState.buildCommand)}`);
result = await execCommand(buildState.buildCommand, {
await execCommand(buildState.buildCommand, {
...spawnOpts,
// Yarn v2 PnP mode may be activated, so force
// "node-modules" linker style
Expand All @@ -282,24 +282,12 @@ export default async function main(client: Client) {
cwd: cwd,
});
} else if (fs.existsSync(join(cwd, 'package.json'))) {
result = await runPackageJsonScript(
await runPackageJsonScript(
client,
cwd,
['vercel-build', 'now-build', 'build'],
spawnOpts
);
} else {
// no package.json exists and no build command present
result = true;
}

if (!result) {
client.output.error(
`Missing required "${cmd(
buildState.buildCommand || 'vercel-build' || 'build'
)}" script in ${param(cwd)}"\n`
);
return 1;
}

if (!fs.existsSync(join(cwd, OUTPUT_DIR))) {
Expand Down Expand Up @@ -550,7 +538,9 @@ export default async function main(client: Client) {
console.log = (...args: any[]) => prefixedLog(prefix, args, origLog);
console.error = (...args: any[]) =>
prefixedLog(prefix, args, origErr);
await plugin.build();
await plugin.build({
workPath: cwd,
});
client.output.debug(
`Completed ${fullName} ${chalk.dim(`${pluginStamp()}`)}`
);
Expand Down
22 changes: 22 additions & 0 deletions packages/cli/src/util/dev/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import deepEqual from 'fast-deep-equal';
import which from 'which';
import npa from 'npm-package-arg';

import { runDevMiddleware } from 'vercel-plugin-middleware';

import { getVercelIgnore, fileNameSymbol } from '@vercel/client';
import {
getTransformedRoutes,
Expand Down Expand Up @@ -1416,6 +1418,26 @@ export default class DevServer {
let prevUrl = req.url;
let prevHeaders: HttpHeadersConfig = {};

const middlewareResult = await runDevMiddleware(req, res, this.cwd);

if (middlewareResult) {
if (middlewareResult.finished) {
return;
}

if (middlewareResult.pathname) {
const origUrl = url.parse(req.url || '/', true);
origUrl.pathname = middlewareResult.pathname;
prevUrl = url.format(origUrl);
}
if (middlewareResult.query && prevUrl) {
const origUrl = url.parse(req.url || '/', true);
delete origUrl.search;
Object.assign(origUrl.query, middlewareResult.query);
prevUrl = url.format(origUrl);
}
}

for (const phase of phases) {
statusCode = undefined;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { response } from './response';

export default () => {
return new Response(response);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const response = 'response';
10 changes: 10 additions & 0 deletions packages/cli/test/fixtures/unit/edge-middleware/_middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import response from './response.json';

export default function () {
return new Response(JSON.stringify(response), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
}
1 change: 1 addition & 0 deletions packages/cli/test/fixtures/unit/edge-middleware/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
not hello world
3 changes: 3 additions & 0 deletions packages/cli/test/fixtures/unit/edge-middleware/response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"text": "hello world"
}
28 changes: 28 additions & 0 deletions packages/cli/test/util/dev/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,32 @@ describe('DevServer', () => {
expect(body).toEqual('The page could not be found.\n\nNOT_FOUND\n');
})
);

it(
'should support edge middleware',
testFixture('edge-middleware', async server => {
const response = await fetch(`${server.address}/index.html`);
const body = await response.json();
expect(body).toEqual(
JSON.parse(
fs.readFileSync(
path.join(
__dirname,
'../../fixtures/unit/edge-middleware/response.json'
),
'utf8'
)
)
);
})
);

it(
'should work with middleware written in typescript',
testFixture('edge-middleware-ts', async server => {
const response = await fetch(`${server.address}/index.html`);
const body = await response.text();
expect(body).toStrictEqual('response');
})
);
});
2 changes: 2 additions & 0 deletions packages/middleware/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
entries.js
dist
2 changes: 2 additions & 0 deletions packages/middleware/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/dist
/test/fixtures/*/.output
25 changes: 25 additions & 0 deletions packages/middleware/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env node
const fs = require('fs-extra');
const execa = require('execa');
const { join } = require('path');

async function main() {
const outDir = join(__dirname, 'dist');

// Start fresh
await fs.remove(outDir);

await execa('tsc', [], {
stdio: 'inherit',
});

await fs.copyFile(
join(__dirname, 'src/entries.js'),
join(outDir, 'entries.js')
);
}

main().catch(err => {
console.error(err);
process.exit(1);
});
59 changes: 59 additions & 0 deletions packages/middleware/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "vercel-plugin-middleware",
"version": "0.0.0-canary.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "",
"repository": {
"type": "git",
"url": "https://github.com/vercel/vercel.git",
"directory": "packages/middleware"
},
"scripts": {
"build": "node build",
"test-unit": "jest",
"prepublishOnly": "node build"
},
"files": [
"dist"
],
"dependencies": {
"@peculiar/webcrypto": "1.2.0",
"cookie": "0.4.1",
"esbuild": "0.13.10",
"formdata-node": "4.3.1",
"globby": "9",
"http-proxy": "1.18.1",
"node-fetch": "^2",
"querystring": "0.2.1",
"ua-parser-js": "1.0.2",
"url": "0.11.0",
"uuid": "8.3.2",
"web-streams-polyfill": "3.1.1"
},
"devDependencies": {
"@types/cookie": "0.4.1",
"@types/glob": "7.2.0",
"@types/http-proxy": "1.17.7",
"@types/jest": "27.0.2",
"@types/node": "16.11.6",
"@types/node-fetch": "^2",
"@types/ua-parser-js": "0.7.36",
"@types/uuid": "8.3.1",
"@vercel/ncc": "0.24.0"
},
"jest": {
"preset": "ts-jest",
"globals": {
"ts-jest": {
"diagnostics": false,
"isolatedModules": true
}
},
"verbose": false,
"testEnvironment": "node",
"testMatch": [
"<rootDir>/test/**/*.test.ts"
]
}
}
15 changes: 15 additions & 0 deletions packages/middleware/src/entries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as middleware from './_middleware';
_ENTRIES = typeof _ENTRIES === 'undefined' ? {} : _ENTRIES;
_ENTRIES['middleware_pages/_middleware'] = {
default: async function (ev) {
const result = await middleware.default(ev.request, ev);
if (result instanceof Response) {
return {
promise: Promise.resolve(),
waitUntil: Promise.resolve(),
response: result,
};
}
return result;
},
};

0 comments on commit 44868d7

Please sign in to comment.