Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: include server assets for Vercel serverless functions #10979

Merged
merged 66 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
cbed243
add ability to copy server assets per route
eltigerchino Nov 4, 2023
4915822
changeset
eltigerchino Nov 4, 2023
fc081d0
fix test
eltigerchino Nov 4, 2023
e45f4ce
fix doc
eltigerchino Nov 4, 2023
045f343
fix asset copying
eltigerchino Nov 4, 2023
eeb1cf5
change process dir when using vite preview
eltigerchino Nov 4, 2023
9ec0f82
remove process.chdir from vite preview
eltigerchino Nov 5, 2023
ac6f3f7
include assets used by default error pages
eltigerchino Nov 6, 2023
9ce6edb
fix doc link
eltigerchino Nov 6, 2023
d8aec4b
better doc example
eltigerchino Nov 6, 2023
ad67ee2
fix doc type
eltigerchino Nov 6, 2023
407af73
ok for real
eltigerchino Nov 6, 2023
df45d57
add tests for vercel adapter
eltigerchino Nov 12, 2023
efa240d
prettier
eltigerchino Nov 12, 2023
816fe44
fix tests
eltigerchino Nov 12, 2023
09a28fb
Merge branch 'master' into feat-server-assets
eltigerchino Nov 12, 2023
7390ed8
fix types
eltigerchino Nov 12, 2023
2c9e86c
more type fixes
eltigerchino Nov 12, 2023
957dc06
try this
eltigerchino Nov 12, 2023
396d48c
documentation
eltigerchino Nov 12, 2023
bb1d891
skip adapter-vercel tests if on node v20
eltigerchino Nov 12, 2023
3449ca2
oopsie wrong PR
eltigerchino Nov 12, 2023
8fbc1aa
move server asset metadata resolving to function
eltigerchino Nov 12, 2023
306dcde
update changesets
eltigerchino Nov 12, 2023
71a722c
revert builder test fixes
eltigerchino Nov 12, 2023
5e09b0b
Merge branch 'main' into feat-server-assets
eltigerchino Dec 21, 2023
7625adb
fix lockfile
eltigerchino Dec 21, 2023
028f35e
prettier
eltigerchino Dec 21, 2023
9401f07
lint
eltigerchino Dec 21, 2023
abafe51
fix vercel tsc errors
eltigerchino Dec 21, 2023
7312bd9
prepublish only
eltigerchino Dec 21, 2023
010081f
fix: avoid running load functions when prerendering if no server load…
eltigerchino Dec 21, 2023
d8dbb21
chore: upgrade eslint-plugin-unicorn (#11432)
benmccann Dec 21, 2023
d75ad7c
Version Packages (#11420)
github-actions[bot] Dec 21, 2023
9d0e3a4
chore(deps): update dependency worktop to v0.8.0-next.16 (#11437)
renovate[bot] Dec 21, 2023
9d93465
i don't think we need this any more (#11439)
Rich-Harris Dec 21, 2023
2355ef6
fix: only disallow dynamic env access when prerendering (#11436)
Rich-Harris Dec 21, 2023
ea274ec
docs: fix links to sveltesociety.dev (#11441)
benmccann Dec 21, 2023
2784729
Version Packages (#11442)
github-actions[bot] Dec 21, 2023
82488f6
fix: set ESLint config type to `Config` instead of `FlatConfig` (#11453)
eltigerchino Dec 23, 2023
928d53b
Version Packages (#11457)
github-actions[bot] Dec 23, 2023
39745e6
fix: improve warning when encountering import.meta.env (#11440)
benmccann Dec 26, 2023
cde2295
empty commit (#11469)
Rich-Harris Dec 26, 2023
7699bfd
fix form actions docs (#11470)
Rich-Harris Dec 26, 2023
c4ee169
Version Packages (#11468)
github-actions[bot] Dec 26, 2023
627cce8
chore(deps): update pnpm to v8.13.1 (#11471)
renovate[bot] Dec 26, 2023
3ac5e78
docs: add performance page (#11424)
benmccann Dec 26, 2023
0e78235
chore(deps): update pnpm to v8.14.0 (#11504)
renovate[bot] Jan 3, 2024
084d97b
feat: use latest Azure adapter in adapter-auto (#11496)
geoffrich Jan 3, 2024
c2f8c84
fix: update @vercel/nft to 0.26.1 (#11508)
benmccann Jan 3, 2024
80a6bab
Version Packages (#11507)
github-actions[bot] Jan 3, 2024
068ded2
exclude universal nodes
eltigerchino Jan 9, 2024
557e4e1
include assets imported by server hooks
eltigerchino Jan 9, 2024
8dcb57c
Merge branch 'main' into feat-server-assets
eltigerchino Jan 9, 2024
125ac3f
update adapter-vercel tests
eltigerchino Jan 11, 2024
fc09cb7
fix tests
eltigerchino Jan 13, 2024
821c673
fix and add test for server hooks assets
eltigerchino Jan 13, 2024
c96c33d
Merge branch 'main' into feat-server-assets
eltigerchino Jan 13, 2024
3b7127a
update adapter-vercel test app packages
eltigerchino Jan 13, 2024
463ffb9
im gonna lose my mind
eltigerchino Jan 13, 2024
cb58b72
ignore hashes for filenames in tests
eltigerchino Jan 13, 2024
6f0fcc0
oops
eltigerchino Jan 13, 2024
88b6800
fix tests?
eltigerchino Jan 13, 2024
3a0929c
revert adapter-vercel types fix
eltigerchino Jan 15, 2024
509a698
improve readability of route asset for loop
eltigerchino Jan 15, 2024
9f03b6b
simplify file reading in serverless functions example
eltigerchino Jan 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/loud-needles-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-vercel': minor
---

feat: bundle server assets with serverless functions
5 changes: 5 additions & 0 deletions .changeset/selfish-bobcats-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': minor
---

feat: adapter method to get server assets used by each route
27 changes: 24 additions & 3 deletions documentation/docs/25-build-and-deploy/80-adapter-netlify.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,29 @@ Additionally, you can add your own Netlify functions by creating a directory for
directory = "functions"
```

## Troubleshooting

### Accessing the file system

You can't access the file system through methods like `fs.readFileSync` in Serverless/Edge environments. If you need to access files that way, do that during building the app through [prerendering](https://kit.svelte.dev/docs/page-options#prerender). If you have a blog for example and don't want to manage your content through a CMS, then you need to prerender the content (or prerender the endpoint from which you get it) and redeploy your blog everytime you add new content.
You can [use files in Netlify Serverless Functions](https://www.netlify.com/blog/2021/08/12/how-to-include-files-in-netlify-serverless-functions/).

```js
// @errors: 2307 7031
/// file: +server.js
import fs from "node:fs";
import path from "node:path";
import { dev } from '$app/environment';

// importing a static asset will return the resolved path in the production build
import PalatinoBoldFont from "$lib/fonts/PalatinoBold.ttf";

const cwd = process.cwd();

// server assets live in `.netlify/server` when deployed to Netlify
const dir = dev ? cwd : path.join(cwd, '.netlify/server');
Comment on lines +126 to +127
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels very hacky. Would love it if we could find a neater solution

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, for Netlify is approximately the same as the official documented Vercel solution - if you use next you can yomp the cwd and a magical '/../' path together, but if you don't, you have to use __dirname + string concatenation. Neither work on kit, so I went back to join(process.cwd(), /foo/bar/baz).

and locally of course it's all completely different anyway, since these are built-time post-adapter generated dirs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll revert this documentation change until there's a better solution for Netlify.


const pathToFile = path.join(dir, PalatinoBoldFont);

export async function GET() {
const file = fs.readFileSync(pathToFile);
// ...
}
```
23 changes: 20 additions & 3 deletions documentation/docs/25-build-and-deploy/90-adapter-vercel.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,25 @@ If you have Vercel functions contained in the `api` directory at the project's r

Projects created before a certain date may default to using an older Node version than what SvelteKit currently requires. You can [change the Node version in your project settings](https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/node-js#node.js-version).

## Troubleshooting

### Accessing the file system

You can't access the file system through methods like `fs.readFileSync` in Serverless/Edge environments. If you need to access files that way, do that during building the app through [prerendering](https://kit.svelte.dev/docs/page-options#prerender). If you have a blog for example and don't want to manage your content through a CMS, then you need to prerender the content (or prerender the endpoint from which you get it) and redeploy your blog everytime you add new content.
You can [use files in Serverless Functions on Vercel](https://vercel.com/guides/how-can-i-use-files-in-serverless-functions).

```js
// @errors: 2307 7031
/// file: api/pdf/+server.js
import fs from "node:fs";
import path from "node:path";

// importing a static asset will return the resolved path in the production build
import PalatinoBoldFont from "$lib/fonts/PalatinoBold.ttf";

const pathToFile = path.join(process.cwd(), PalatinoBoldFont);

export async function GET() {
const file = fs.readFileSync(pathToFile);
// ...
}
```

> Only assets that are imported in `+page.server`, `+layout.server` and `+server` files are included in the Serverless Function bundle.
eltigerchino marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions packages/adapter-vercel/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
node_modules
.vercel
49 changes: 36 additions & 13 deletions packages/adapter-vercel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ const plugin = function (defaults = {}) {

/**
* @param {string} name
* @param {import('.').ServerlessConfig} config
* @param {import('@sveltejs/kit').RouteDefinition<import('.').Config>[]} routes
* @param {import('./index.js').ServerlessConfig} config
* @param {import('@sveltejs/kit').RouteDefinition<import('./index.js').Config>[]} routes
*/
async function generate_serverless_function(name, config, routes) {
const relativePath = path.posix.relative(tmp, builder.getServerDirectory());
Expand All @@ -81,14 +81,15 @@ const plugin = function (defaults = {}) {
builder,
`${tmp}/index.js`,
`${dirs.functions}/${name}.func`,
config
config,
routes
);
}

/**
* @param {string} name
* @param {import('.').EdgeConfig} config
* @param {import('@sveltejs/kit').RouteDefinition<import('.').EdgeConfig>[]} routes
* @param {import('./index.js').EdgeConfig} config
* @param {import('@sveltejs/kit').RouteDefinition<import('./index.js').EdgeConfig>[]} routes
*/
async function generate_edge_function(name, config, routes) {
const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`);
Expand Down Expand Up @@ -135,7 +136,7 @@ const plugin = function (defaults = {}) {
);
}

/** @type {Map<string, { i: number, config: import('.').Config, routes: import('@sveltejs/kit').RouteDefinition<import('.').Config>[] }>} */
/** @type {Map<string, { i: number, config: import('./index.js').Config, routes: import('@sveltejs/kit').RouteDefinition<import('./index.js').Config>[] }>} */
const groups = new Map();

/** @type {Map<string, { hash: string, route_id: string }>} */
Expand All @@ -144,7 +145,7 @@ const plugin = function (defaults = {}) {
/** @type {Map<string, string>} */
const functions = new Map();

/** @type {Map<import('@sveltejs/kit').RouteDefinition<import('.').Config>, { expiration: number | false, bypassToken: string | undefined, allowQuery: string[], group: number, passQuery: true }>} */
/** @type {Map<import('@sveltejs/kit').RouteDefinition<import('./index.js').Config>, { expiration: number | false, bypassToken: string | undefined, allowQuery: string[], group: number, passQuery: true }>} */
const isr_config = new Map();

/** @type {Set<string>} */
Expand All @@ -163,7 +164,7 @@ const plugin = function (defaults = {}) {
}

const node_runtime = /nodejs([0-9]+)\.x/.exec(runtime);
if (runtime !== 'edge' && (!node_runtime || node_runtime[1] < 18)) {
if (runtime !== 'edge' && (!node_runtime || +node_runtime[1] < 18)) {
throw new Error(
`Invalid runtime '${runtime}' for route ${route.id}. Valid runtimes are 'edge' and 'nodejs18.x' or higher ` +
'(see the Node.js Version section in your Vercel project settings for info on the currently supported versions).'
Expand Down Expand Up @@ -368,7 +369,7 @@ function write(file, data) {
// This function is duplicated in adapter-static
/**
* @param {import('@sveltejs/kit').Builder} builder
* @param {import('.').Config} config
* @param {import('index.js').Config} config
*/
function static_vercel_config(builder, config) {
/** @type {any[]} */
Expand All @@ -377,8 +378,11 @@ function static_vercel_config(builder, config) {
/** @type {Record<string, { path: string }>} */
const overrides = {};

/** @type {import('./index').ImagesConfig} */
const images = config.images;
/** @type {import('./index.js').ImagesConfig | undefined} */
let images;
if (config.runtime !== 'edge') {
images = /** @type {import('./index.js').ServerlessConfig} */ (config).images;
}
Comment on lines +383 to +385
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this change about?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I was trying to resolve some type issues in the adapter but I didn't fully revert those changes. I'll revert them properly and defer them to another PR


for (const [src, redirect] of builder.prerendered.redirects) {
prerendered_redirects.push({
Expand Down Expand Up @@ -434,9 +438,10 @@ function static_vercel_config(builder, config) {
* @param {import('@sveltejs/kit').Builder} builder
* @param {string} entry
* @param {string} dir
* @param {import('.').ServerlessConfig} config
* @param {import('./index.js').ServerlessConfig} config
* @param {import('@sveltejs/kit').RouteDefinition[]} routes
*/
async function create_function_bundle(builder, entry, dir, config) {
async function create_function_bundle(builder, entry, dir, config, routes) {
fs.rmSync(dir, { force: true, recursive: true });

let base = entry;
Expand Down Expand Up @@ -544,6 +549,24 @@ async function create_function_bundle(builder, entry, dir, config) {
)
);

const server_assets = builder.getServerAssets();
eltigerchino marked this conversation as resolved.
Show resolved Hide resolved
let routes_assets = new Set(server_assets.rootErrorPage);

for (const route of routes) {
const assets = server_assets.routes.get(route.id);
if (assets) {
routes_assets = new Set([...routes_assets, ...assets]);
}
}

if (server_assets.hooks) {
routes_assets = new Set([...routes_assets, ...server_assets.hooks]);
}

for (const asset of routes_assets) {
builder.copy(path.join(builder.getServerDirectory(), asset), path.join(dir, asset));
}

write(`${dir}/package.json`, JSON.stringify({ type: 'module' }));
}

Expand Down
3 changes: 2 additions & 1 deletion packages/adapter-vercel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@
"lint": "prettier --check .",
"format": "pnpm lint --write",
"check": "tsc",
"test": "vitest run"
"test": "vitest run & pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test"
},
"dependencies": {
"@vercel/nft": "^0.26.1",
"esbuild": "^0.19.9"
},
"devDependencies": {
"@playwright/test": "1.30.0",
"@sveltejs/kit": "workspace:^",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@types/node": "^18.19.3",
Expand Down
10 changes: 10 additions & 0 deletions packages/adapter-vercel/test/apps/split/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
1 change: 1 addition & 0 deletions packages/adapter-vercel/test/apps/split/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
18 changes: 18 additions & 0 deletions packages/adapter-vercel/test/apps/split/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "~TODO~",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"test": "playwright test"
},
"devDependencies": {
"@sveltejs/kit": "workspace:^",
"svelte": "^4.2.8",
"typescript": "^5.3.3",
"vite": "^5.0.8"
},
"type": "module"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { config as default } from '../../utils.js';
12 changes: 12 additions & 0 deletions packages/adapter-vercel/test/apps/split/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}

export {};
12 changes: 12 additions & 0 deletions packages/adapter-vercel/test/apps/split/src/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
6 changes: 6 additions & 0 deletions packages/adapter-vercel/test/apps/split/src/hooks.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import text from '$lib/hooks.server.js.txt';

export async function handle({ event, resolve }) {
event.setHeaders({ 'x-server-asset': text });
return resolve(event);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+error.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+layout.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+page.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+server.js
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hooks.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root_error
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root_layout
3 changes: 3 additions & 0 deletions packages/adapter-vercel/test/apps/split/src/lib/transitive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import text from './transitive.txt';

export { text };
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
transitive
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
import asset from '$lib/root_error.txt';

/** @type {import('./$types').PageData}*/
export let data;
</script>

<h1>{data}</h1>
<p>{asset}</p>
5 changes: 5 additions & 0 deletions packages/adapter-vercel/test/apps/split/src/routes/+layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import layout from '$lib/+layout.js.txt';

export function load({ data }) {
return { ...data, layout };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import layout_server from '$lib/+layout.server.js.txt';

export function load() {
return { layout_server };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { text } from '$lib/transitive';

export function load() {
return { text };
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import server_asset from '$lib/+server.js.txt';

export function GET() {
return new Response(server_asset);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import asset from '$lib/+error.svelte.txt';
</script>

<p>{asset}</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import layout from '$lib/+layout.js.txt';

export function load() {
return { layout };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
import asset from '$lib/+layout.svelte.txt';
</script>

<p>{asset}</p>

<slot />
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import page_data from '$lib/+page.js.txt';

export async function load({ parent, data }) {
return { ...data, ...(await parent()), page_data };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import page_server_text from '$lib/+page.server.js.txt';

export async function load({ parent }) {
return { ...(await parent()), page_server_text };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import asset from '$lib/+page.svelte.txt';
</script>

<p>{asset}</p>
Loading
Loading