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

Mux Uploader Server Component #913

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

decepulis
Copy link
Contributor

@decepulis decepulis commented Apr 25, 2024

Let's take another shot at #712...

How to use it

import MuxUploader, { other, named, imports } from '@mux/mux-uploader-react/rsc';

Defaults

If no endpoint is specified, will use @mux/mux-node to create a direct upload with the following configuration:

{
  cors_origin: '*',
  new_asset_settings: {
    playback_policy: ['public'],
    encoding_tier: 'baseline',
}

How to configure

Accepts all the same params as Mux Uploader, as well as

interface MuxUploaderServerProps extends MuxUploaderProps {
  clientOptions?: ClientOptions; // allows you to configure the @mux/mux-node client
  uploadCreateParams?: Partial<Mux.Video.Uploads.UploadCreateParams>; // allows you to configure the direct upload
  uploadRequestOptions?: Partial<Mux.RequestOptions>; // allows you to configure the direct upload request
}

uploadCreateParams will be deep-merged with the default options described above

How it gets bundled

"build:rsc:cjs": "esbuild src/rsc.tsx --target=es2019 --platform=node --bundle --sourcemap --metafile=./dist/cjs-rsc.json --format=cjs --loader:.css=text --outdir=dist --out-extension:.js=.cjs.js --external:react --external:prop-types --external:server-only --external:./index",
"build:rsc:esm": "esbuild src/rsc.tsx --target=es2019 --platform=node --bundle --sourcemap --metafile=./dist/esm-rsc.json --format=esm --loader:.css=text --outdir=dist --out-extension:.js=.mjs --external:react --external:prop-types --external:server-only --external:./index",

lemme point out some interesting flags, here:

  • --platform=node: in order to avoid bundling dependencies like crypto, esbuild requires this of us. Luckily, Server Components run on the server, so, this is correct
  • --external:server-only: the contents of this package is literally just throw new Error('This is a server component and can't be imported in a client component') (paraphrased). If we were to bundle it, our bundle would immediately throw an error. I suspect that, in practice, server-component-compliant bundlers will know to ignore this import. Even so, should server-only be marked as a peer dependency? 🤔
  • --external:./index: In order for server component bundlers to leave the server stuff on the server and the client stuff on the client, we have to ship the server bundle and the client bundle separately. By marking index as external, we're telling esbuild "don't include the client bundle in the server bundle". Instead, consumer bundlers will see this in dist/rsc.mjs and resolve dist/index.mjs (or dist/rsc.cjs.js and dist/index.cjs.js respectively).

TODO items:

  • get some feedback
  • consider this as a way to export the module
  • documentation
  • two questions for those of you who understand bundlers better than I do...
    • am I correct about what I did with --external:server-only and --external:./index? This seems to work in our test Next app, so that's a good sign...
    • why is @mux/mux-uploader-react/rsc giving me a red squiggle in the nextjs example? why can't typescript find it? or is that just something in my environment?

Other passing thoughts

I don't re-export the types, so right now if you want those, you'll have to import type { ... } from '@mux/mux-uploader-react'. TypeScript should pick up on that; I'm not worried

## How it works
### Defaults
If no `endpoint` is specified, will use `@mux/mux-node` to create a direct upload with the following configuration:
```ts
{
  cors_origin: '*',
  new_asset_settings: {
    playback_policy: ['public'],
    encoding_tier: 'baseline',
}
```
### How to configure
Accepts all the same params as Mux Uploader, as well as
```ts
interface MuxUploaderServerProps extends MuxUploaderProps {
  clientOptions?: ClientOptions;
  uploadCreateParams?: Partial<Mux.Video.Uploads.UploadCreateParams>;
  uploadRequestOptions?: Partial<Mux.RequestOptions>;
}
```
`uploadCreateParams` will be deep-merged with the default options described above

## TODO items:
- [ ] get some feedback
- [ ] answer some questions 👇
- [ ] documentation

## Outstanding questions:
for those of you who know esbuild and bundlers better than I do:
- [ ] why is @mux/mux-uploader-react/rsc giving me a red squiggle in the nextjs example? why can't typescript find it?
- [ ] is it correct to external:server-only even though it's not a peer dependency?
@decepulis decepulis requested a review from a team as a code owner April 25, 2024 15:56
Copy link

vercel bot commented Apr 25, 2024

@decepulis is attempting to deploy a commit to the Mux Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

vercel bot commented Apr 25, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
elements-demo-create-react-app ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 25, 2024 4:46pm
elements-demo-nextjs ❌ Failed (Inspect) Apr 25, 2024 4:46pm
elements-demo-svelte-kit ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 25, 2024 4:46pm
elements-demo-vanilla ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 25, 2024 4:46pm
elements-demo-vue ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 25, 2024 4:46pm

@mmcc
Copy link
Contributor

mmcc commented Jul 15, 2024

What could it look like for this component (or set of them) to be able to return things like an asset ID / playback ID?

@decepulis
Copy link
Contributor Author

Running into a huge blocker with this. Consider the following:

import MuxUploader from '@mux/mux-uploader-react/rsc';

function MuxUploaderPage() {
  return (
    <MuxUploader onSuccess={console.log} />
  );
}

export default MuxUploaderPage;

However, you can't pass client functionality (in our example, console.log) to server components. Props have to be serializable.

Gotta think of another interface if we want this to happen...

@decepulis
Copy link
Contributor Author

decepulis commented Jul 31, 2024

What could it look like for this component (or set of them) to be able to return things like an asset ID / playback ID?

@mmcc btw just to answer this. Server Actions would make this really slick. Under the hood, we could implement something like this:

// packages/mux-uploader-react
import 'server-only'
import MuxUploaderClient from './index'
 
const MuxUploaderServer = (props) => {
  const upload = await mux.video.uploads.create(...);
  return (
    <MuxUploaderClient
      endpoint={upload.url}
      onSuccess={
        "use server"
        const assetId = await getAssetIdForUpload(upload.id)
        props.onAssetId(assetId)
      }
    />
)
}

And it gets more exciting.
We could even poll the asset API to provide a UI that updates with the asset preparing status. Cool stuff!

I've actually got this implemented in another branch.

Just gotta think through... my previous comment...

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.

2 participants