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

implement serverAssets #11647

Closed
wants to merge 3 commits into from
Closed

implement serverAssets #11647

wants to merge 3 commits into from

Conversation

Rich-Harris
Copy link
Member

This might not go anywhere, I'm just putting it here for posterity.

It implements the serverAssets idea mentioned in #11020 (comment). If adapters set the value correctly (and include the assets in functions, where applicable — see #10979), this would allow Node-based deployments to read stuff from the filesystem:

import fs from 'node:fs';
import file from './file.txt';
import { serverAssets } from '$app/paths';

export function GET() {
  return new Response(fs.readFileSync(`${serverAssets}/${file}`, 'utf-8'));
}

There are two problems:

  1. It would be nice to have a more platform-agnostic approach, if possible (i.e. use node:fs in dev/preview, but a platform-native approach elsewhere)
  2. You have to be very careful to avoid assets being inlined. fs.readFile(`${serverAssets}/data:...`) doesn't work

We could maybe solve the second part by forcing assetsInlineLimit to 0 in SSR mode, but this would result in mismatches in cases like this, if the asset was inlined in the client build:

<script>
  import icon from '$lib/icons/whatever.png';
</script>

<img src={icon} alt="icon" />

Maybe that's fine though?

For the platform-agnostic thing, we'd presumably need to have something like this:

import { text } from '$app/fs';
import file from './file.txt';

export async function GET() {
  return new Response(await text(file));
}

Pros of a dedicated $app/fs module:

  • Platform-agnostic (within reason)
  • Can handle data: URLs
  • More convenient APIs (await text(...), await json(...), streaming, etc — maybe we could even offer helpers for creating Response objects with correct content-type etc?)
  • We can know which utilities are imported in which contexts, which would be helpful for e.g. saying 'you can't read from the filesystem in an edge function' (though this wouldn't account for inlined assets)

Costs:

  • Increased surface area
  • We're not likely to replicate the entirety of node:fs

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

Edits

  • Please ensure that 'Allow edits from maintainers' is checked. PRs without this option may be closed.

Copy link

changeset-bot bot commented Jan 16, 2024

⚠️ No Changeset found

Latest commit: 95279b4

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@Rich-Harris
Copy link
Member Author

Been sitting with this for a bit, and maybe 'we're not likely to replicate the entirety of node:fs' is a feature, rather than a bug?

Given that we're talking about assets that are transformed/manipulated at build time, there's no world in which it would make sense to write to them, or copy them, or symlink them, or stat them, or any of the other things you can do with fs. The only thing you could want to do with them is read them.

And if the API is nice enough, it needn't feel like an expansion of the surface area. What if we had a single function, readFile, that returned a Response object (with a streamable body and the appropriate Content-Type etc)?

import { readFile } from '$app/server';
import blah from './file.txt';

export function GET() {
  return readFile(blah);
}

If you needed to read the body inside the function, you could easily do so:

import { readFile } from '$app/server';
import blah from './file.txt';

export function GET() {
  const file = readFile(blah);
  const text = await file.text(); // or `await file.json()` or `await file.arrayBuffer()` or whatever

  return new Response(text.replace('red', 'blue'), {
    headers: file.headers
  });
}

The name is bikesheddable. readAsset, to underscore that it only works with imported assets? getAsset? Just asset?

If a given adapter didn't provide an implementation, the function would throw when you use it (or maybe on app startup or even build time, if we detect that it's used?)

@Rich-Harris Rich-Harris mentioned this pull request Jan 16, 2024
13 tasks
@Rich-Harris
Copy link
Member Author

closing in favour of #11649

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant