Skip to content

A way to mark certain dependencies as client-side #49

@LeaVerou

Description

@LeaVerou

As discussed in this Slack thread:

There is already ample precedent on marking dependencies differently based on the context they are needed in, with devDependencies being the most popular (but there is also optionalDependencies, peerDependencies, peerDependenciesMeta, bundledDependencies, bundleDependencies, etc, though I’m not sure what's their standardization status. However, we have nothing to distinguish server runtime deps from client runtime deps.

Why does this matter?

  • Because often hosts will not deploy node_modules at all (e.g. Netlify and Vercel both do this).
  • Because in the absence of it, tooling (e.g. Next.js) assumes that every dependency is a client-side dependency, and will attempt to bundle them.

Yes, most people these days use bundlers, but many do so begrudgingly, because the alternative is simply not viable. Something like this is a step towards not needing third-party tools for basic dependency management. Browsers support ESM naturally. Import maps allow mapping specifiers to URLs (with clunky ergonomics today, but hopefully this will be fixed). The last piece of the puzzle is knowing which dependencies to deploy, which is currently impossible. Import maps could even be generated automatically from this subtree.

I’m not sure what the right syntax would be. The path of least resistance seems to be a separate clientDependencies field, modeled after devDependencies. But it seems there is a combinatorial explosion of dependency categories (e.g. @ljharb wants optionalDevDependencies) which begs the question whether the right data structure is different, one where dependencies are declared once and assigned metadata:

{
	"dependencies": {
		"foo": "^17.2.1",
		"bar": {
			"version": "^1.0.1",
			"devOnly": true,
			"client": true
		}
}

Some things to note:

  • This should not be a per-project setting, but a per-package setting. Many (most?) projects run server-side code, built-time code, AND client-side code.
  • A package being used client-side is not mutually exclusive with other uses. For a trivial example: utility libraries (e.g. lodash) may be used across server, client, AND build tools. This is not even particularly complex. Suppose you have a serverless function to access an API, a build process, and UI components, you now have all three. And that's a pretty basic web app.
  • This should be decided by the package consumer, not the package author. Perhaps client-side dependencies that are not also dependencies should not even be installed when installing a package. Again devDependencies is good precedent here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions