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

[Float][Fizz][Static] add importMap option to Fizz and Static server renderers #27260

Merged
merged 1 commit into from
Aug 24, 2023

Conversation

gnoff
Copy link
Collaborator

@gnoff gnoff commented Aug 22, 2023

Import maps need to be emitted before any scripts or preloads so the browser can properly locate these resources.

Unlike most scripts, importmaps are singletons meaning you can only have one per document and they must appear before any modules are loaded or preloaded. In the future there may be a way to dynamically add more mappings however the proposed API for this seems likely to be a javascript API and not an html tag.

Given the unique constraints here this PR implements React's support of importMaps as the following

  1. an importMap option accepting a plain object mapping module specifier to path is accepted in any API that renders a preamble (head content). Notably this precludes resume rendering because in resume cases the preamble should have already been produced as part of the prerender step.
  2. the importMap is stringified and emitted as a <script type="importmap">...</script> in the preamble.
  3. the importMap is escaped identically to how bootstrapScriptContent is escaped, notably, isntances of </script> are escaped to avoid breaking out of the script context

Users can still render importmap tags however with Float enabled this is rather pointless as most modules will be hoisted above the importmap that is rendered. In practice this means the only functional way to use import maps with React is to use this config API.

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Aug 22, 2023
@react-sizebot
Copy link

react-sizebot commented Aug 22, 2023

Comparing: 31034b6...58b402f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 165.59 kB 165.59 kB = 51.88 kB 51.88 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 173.15 kB 173.15 kB = 54.20 kB 54.20 kB
facebook-www/ReactDOM-prod.classic.js = 569.82 kB 569.82 kB = 100.37 kB 100.37 kB
facebook-www/ReactDOM-prod.modern.js = 553.62 kB 553.62 kB = 97.53 kB 97.53 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.min.js +0.41% 63.38 kB 63.64 kB +0.36% 19.60 kB 19.67 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.min.js +0.41% 63.40 kB 63.66 kB +0.35% 19.62 kB 19.69 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js +0.41% 65.55 kB 65.82 kB +0.33% 20.25 kB 20.31 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.production.min.js +0.41% 63.53 kB 63.79 kB +0.34% 19.87 kB 19.93 kB
oss-stable/react-dom/umd/react-dom-server.browser.production.min.js +0.41% 63.56 kB 63.81 kB +0.34% 19.89 kB 19.96 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js +0.40% 65.69 kB 65.96 kB +0.53% 20.46 kB 20.57 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.production.min.js +0.40% 64.76 kB 65.02 kB +0.32% 20.11 kB 20.18 kB
oss-experimental/react-dom/cjs/react-dom-static.edge.production.min.js +0.40% 65.08 kB 65.35 kB +0.40% 20.23 kB 20.31 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.production.min.js +0.39% 67.61 kB 67.87 kB +0.32% 21.02 kB 21.09 kB
oss-stable/react-dom/cjs/react-dom-server.edge.production.min.js +0.39% 67.63 kB 67.90 kB +0.31% 21.05 kB 21.11 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.min.js +0.39% 69.85 kB 70.12 kB +0.39% 21.72 kB 21.80 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js +0.38% 70.00 kB 70.27 kB +0.38% 21.80 kB 21.88 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.min.js +0.38% 67.67 kB 67.93 kB +0.37% 21.03 kB 21.11 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.min.js +0.38% 67.70 kB 67.95 kB +0.38% 21.05 kB 21.13 kB
oss-experimental/react-dom/cjs/react-dom-static.node.production.min.js +0.38% 69.29 kB 69.55 kB +0.30% 21.67 kB 21.74 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.min.js +0.36% 65.75 kB 65.99 kB +0.33% 20.02 kB 20.09 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.min.js +0.36% 65.78 kB 66.01 kB +0.33% 20.05 kB 20.11 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.min.js +0.35% 67.91 kB 68.15 kB +0.28% 20.87 kB 20.93 kB
facebook-www/ReactDOMServer-dev.modern.js +0.32% 354.72 kB 355.86 kB +0.35% 78.46 kB 78.74 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.31% 346.40 kB 347.49 kB +0.34% 77.66 kB 77.93 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.31% 346.43 kB 347.52 kB +0.35% 77.69 kB 77.96 kB
facebook-www/ReactDOMServer-dev.classic.js +0.31% 362.15 kB 363.29 kB +0.36% 80.10 kB 80.39 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.development.js +0.31% 343.85 kB 344.93 kB +0.34% 77.19 kB 77.45 kB
oss-stable/react-dom/cjs/react-dom-server.bun.development.js +0.31% 343.88 kB 344.96 kB +0.34% 77.22 kB 77.48 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +0.31% 348.31 kB 349.40 kB +0.34% 78.12 kB 78.39 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +0.31% 348.34 kB 349.43 kB +0.35% 78.15 kB 78.42 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +0.31% 346.63 kB 347.71 kB +0.35% 78.09 kB 78.36 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +0.31% 346.65 kB 347.73 kB +0.35% 78.12 kB 78.39 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.development.js +0.31% 347.04 kB 348.12 kB +0.35% 78.22 kB 78.49 kB
oss-stable/react-dom/cjs/react-dom-server.edge.development.js +0.31% 347.06 kB 348.14 kB +0.35% 78.25 kB 78.52 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.development.js +0.31% 363.04 kB 364.17 kB +0.35% 78.49 kB 78.76 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.development.js +0.31% 363.07 kB 364.20 kB +0.35% 78.51 kB 78.79 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +0.31% 349.96 kB 351.05 kB +0.35% 77.32 kB 77.59 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +0.31% 348.11 kB 349.19 kB +0.35% 78.13 kB 78.40 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +0.31% 348.13 kB 349.21 kB +0.35% 78.16 kB 78.43 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +0.31% 358.64 kB 359.75 kB +0.36% 80.25 kB 80.53 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.development.js +0.31% 359.05 kB 360.16 kB +0.36% 80.38 kB 80.66 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +0.31% 359.58 kB 360.68 kB +0.36% 80.29 kB 80.58 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.development.js +0.31% 363.27 kB 364.39 kB +0.34% 78.96 kB 79.23 kB
oss-stable/react-dom/umd/react-dom-server.browser.development.js +0.31% 363.30 kB 364.42 kB +0.33% 78.99 kB 79.25 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.development.js +0.31% 356.12 kB 357.21 kB +0.35% 80.04 kB 80.32 kB
oss-experimental/react-dom/cjs/react-dom-static.edge.development.js +0.31% 356.53 kB 357.62 kB +0.35% 80.17 kB 80.45 kB
oss-experimental/react-dom/umd/react-dom-server.browser.development.js +0.31% 375.82 kB 376.97 kB +0.35% 81.12 kB 81.40 kB
oss-experimental/react-dom/cjs/react-dom-static.node.development.js +0.31% 358.26 kB 359.36 kB +0.35% 80.36 kB 80.64 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.31% 357.04 kB 358.13 kB +0.36% 79.97 kB 80.26 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +0.30% 354.49 kB 355.58 kB +0.34% 79.50 kB 79.77 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +0.30% 358.95 kB 360.04 kB +0.35% 80.43 kB 80.71 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.development.js +0.30% 374.13 kB 375.26 kB +0.34% 80.81 kB 81.09 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +0.22% 156.20 kB 156.55 kB +0.13% 28.80 kB 28.84 kB
facebook-www/ReactDOMServer-prod.modern.js +0.22% 154.08 kB 154.42 kB +0.15% 27.87 kB 27.91 kB
facebook-www/ReactDOMServer-prod.classic.js +0.22% 154.81 kB 155.15 kB +0.15% 28.10 kB 28.14 kB

Generated by 🚫 dangerJS against 58b402f

Copy link

@AbdullahWins AbdullahWins left a comment

Choose a reason for hiding this comment

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

well done!

@gnoff gnoff changed the title [Float][Fizz][Fiber] make <script type="importmap">...</script> hoistable [Float][Fizz][Fiber] add importMap option to renderToReadableStream, renderToPipeableStream, and prerenderToNodeStream Aug 22, 2023
@gnoff gnoff force-pushed the float-import-maps branch 3 times, most recently from 71bd635 to 7b65915 Compare August 22, 2023 21:43
@gnoff gnoff changed the title [Float][Fizz][Fiber] add importMap option to renderToReadableStream, renderToPipeableStream, and prerenderToNodeStream [Float][Fizz][Static] add importMap option to Fizz and Static server renderers Aug 22, 2023
…browser can properly locate these resources. This change makes React aware of the concept of import maps and emits them before scripts and modules and their preloads.
Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

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

There's currently an issue with externalRuntimeScript because it has non-serializable chunks.

However, in a world where we haven't yet flushed the shell we might not have emitted it.

Should external runtime and import maps always go into the preamble in this case? What if it's headers only?

I'm not sure we can be sure that this has flushed completely at the end of the prerender. So maybe this needs to be part of resumable state?

Might have to do something similar with externalRuntimeScript where the chunks are lazily generated.

@gnoff
Copy link
Collaborator Author

gnoff commented Aug 23, 2023

I was operating under the assumption that if prerendering did not error the shell was done. but we discussed how the preamble could be extracted even when the shell wasn't ready. In this case yes I think we should assume the importMap and externalRuntime should be part of the preamble if we've encountered our <html> element (or we know we won't get one in the case of prerenderDocument and we see some other tag first). If we don't see our <html> I think we should consider emitting headers only but we'd have to fall back to not preloading modules b/c the map wouldn't be ready. So maybe in that case you get best effort headers but then we just do a regular render rather than a resume. this would avoid having to do lazy chunk creation or figure out how to serialize chunks

@gnoff gnoff merged commit 9d4582d into facebook:main Aug 24, 2023
2 checks passed
@gnoff gnoff deleted the float-import-maps branch August 24, 2023 20:48
github-actions bot pushed a commit that referenced this pull request Aug 24, 2023
…r renderers (#27260)

Import maps need to be emitted before any scripts or preloads so the
browser can properly locate these resources.

Unlike most scripts, importmaps are singletons meaning you can only have
one per document and they must appear before any modules are loaded or
preloaded. In the future there may be a way to dynamically add more
mappings however the proposed API for this seems likely to be a
javascript API and not an html tag.

Given the unique constraints here this PR implements React's support of
importMaps as the following

1. an `importMap` option accepting a plain object mapping module
specifier to path is accepted in any API that renders a preamble (head
content). Notably this precludes resume rendering because in resume
cases the preamble should have already been produced as part of the
prerender step.
2. the importMap is stringified and emitted as a `<script
type="importmap">...</script>` in the preamble.
3. the importMap is escaped identically to how bootstrapScriptContent is
escaped, notably, isntances of `</script>` are escaped to avoid breaking
out of the script context

Users can still render importmap tags however with Float enabled this is
rather pointless as most modules will be hoisted above the importmap
that is rendered. In practice this means the only functional way to use
import maps with React is to use this config API.

DiffTrain build for [9d4582d](9d4582d)
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…r renderers (facebook#27260)

Import maps need to be emitted before any scripts or preloads so the
browser can properly locate these resources.

Unlike most scripts, importmaps are singletons meaning you can only have
one per document and they must appear before any modules are loaded or
preloaded. In the future there may be a way to dynamically add more
mappings however the proposed API for this seems likely to be a
javascript API and not an html tag.

Given the unique constraints here this PR implements React's support of
importMaps as the following

1. an `importMap` option accepting a plain object mapping module
specifier to path is accepted in any API that renders a preamble (head
content). Notably this precludes resume rendering because in resume
cases the preamble should have already been produced as part of the
prerender step.
2. the importMap is stringified and emitted as a `<script
type="importmap">...</script>` in the preamble.
3. the importMap is escaped identically to how bootstrapScriptContent is
escaped, notably, isntances of `</script>` are escaped to avoid breaking
out of the script context

Users can still render importmap tags however with Float enabled this is
rather pointless as most modules will be hoisted above the importmap
that is rendered. In practice this means the only functional way to use
import maps with React is to use this config API.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
…r renderers (#27260)

Import maps need to be emitted before any scripts or preloads so the
browser can properly locate these resources.

Unlike most scripts, importmaps are singletons meaning you can only have
one per document and they must appear before any modules are loaded or
preloaded. In the future there may be a way to dynamically add more
mappings however the proposed API for this seems likely to be a
javascript API and not an html tag.

Given the unique constraints here this PR implements React's support of
importMaps as the following

1. an `importMap` option accepting a plain object mapping module
specifier to path is accepted in any API that renders a preamble (head
content). Notably this precludes resume rendering because in resume
cases the preamble should have already been produced as part of the
prerender step.
2. the importMap is stringified and emitted as a `<script
type="importmap">...</script>` in the preamble.
3. the importMap is escaped identically to how bootstrapScriptContent is
escaped, notably, isntances of `</script>` are escaped to avoid breaking
out of the script context

Users can still render importmap tags however with Float enabled this is
rather pointless as most modules will be hoisted above the importmap
that is rendered. In practice this means the only functional way to use
import maps with React is to use this config API.

DiffTrain build for commit 9d4582d.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants