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

fix(dev): pass script props to deferred inline script tags #6389

Merged
merged 9 commits into from
Jun 14, 2023

Conversation

jack-r-warren
Copy link
Contributor

@jack-r-warren jack-r-warren commented May 14, 2023

Closes #5156, Closes #5539

If you pass a nonce or some other prop to <Scripts />, it doesn't get added to all of the script tags that might be written — the ones that handle deferred data won't get it. This PR just passes the props all the way down (see b2be332).

  • Docs

Don't think there are doc changes needed — this is a bug fix aligning with what's already documented at the bottom of this page.

  • Tests

Testing Strategy: I added a test in fe7d61e.

Testing this was really tricky, the entire <Scripts /> component seemed untested. Happy for feedback:

  • <Scripts /> gates most of its behavior behind a un-exported global isHydrated variable that Jest can't clean up, so we get one clean call to RTL's render per file (subsequent ones skip all script tags)
  • There's deferred script behavior conditional on document being undefined, but RTL uses document internally, so there's some cases I'm fixing that I wasn't able to test
  • I didn't want to tightly couple this test to the data loading implementation details of Remix / React Router (not really what we're looking to test here), so I thought that allowing some type errors in the Remix EntryContext would strike the right balance

@changeset-bot
Copy link

changeset-bot bot commented May 14, 2023

🦋 Changeset detected

Latest commit: b6ae4f9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 18 packages
Name Type
@remix-run/react Patch
@remix-run/testing Patch
create-remix Patch
remix Patch
@remix-run/architect Patch
@remix-run/cloudflare Patch
@remix-run/cloudflare-pages Patch
@remix-run/cloudflare-workers Patch
@remix-run/css-bundle Patch
@remix-run/deno Patch
@remix-run/dev Patch
@remix-run/eslint-config Patch
@remix-run/express Patch
@remix-run/netlify Patch
@remix-run/node Patch
@remix-run/serve Patch
@remix-run/server-runtime Patch
@remix-run/vercel Patch

Not sure what this means? Click here to learn what changesets are.

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

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented May 14, 2023

Hi @jack-r-warren,

Welcome, and thank you for contributing to Remix!

Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once.

You may review the CLA and sign it by adding your name to contributors.yml.

Once the CLA is signed, the CLA Signed label will be added to the pull request.

If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at hello@remix.run.

Thanks!

- The Remix team

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented May 14, 2023

Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳

@jack-r-warren
Copy link
Contributor Author

If anyone's watching and has ideas on how to get this (or some other fix) merged, I'm all ears. This is blocking feature development on our end (we can't defer with a content security policy) and I'm happy for anything I can do to help this along.

@kentcdodds
Copy link
Member

I've got a patch with a fix for this if anyone else needs to get unblocked:

patch
diff --git a/node_modules/@remix-run/react/dist/components.js b/node_modules/@remix-run/react/dist/components.js
index 79b255b..cf5b030 100644
--- a/node_modules/@remix-run/react/dist/components.js
+++ b/node_modules/@remix-run/react/dist/components.js
@@ -675,7 +675,8 @@ function Scripts(props) {
             key: `${routeId} | ${key}`,
             deferredData: deferredData,
             routeId: routeId,
-            dataKey: key
+            dataKey: key,
+            nonce: props.nonce
           }));
           return `${JSON.stringify(key)}:__remixContext.n(${JSON.stringify(routeId)}, ${JSON.stringify(key)})`;
         } else {
@@ -705,10 +706,12 @@ window.__remixRouteModules = {${matches.map((match, index) => `${JSON.stringify(
 
 import(${JSON.stringify(manifest.entry.module)});`;
     return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("script", _rollupPluginBabelHelpers["extends"]({}, props, {
+      nonce: props.nonce,
       suppressHydrationWarning: true,
       dangerouslySetInnerHTML: markup.createHtml(contextScript),
       type: undefined
     })), /*#__PURE__*/React__namespace.createElement("script", _rollupPluginBabelHelpers["extends"]({}, props, {
+      nonce: props.nonce,
       suppressHydrationWarning: true,
       dangerouslySetInnerHTML: markup.createHtml(routeModulesScript),
       type: "module",
@@ -722,6 +725,7 @@ import(${JSON.stringify(manifest.entry.module)});`;
   if (!isStatic && typeof __remixContext === "object" && __remixContext.a) {
     for (let i = 0; i < __remixContext.a; i++) {
       deferredScripts.push( /*#__PURE__*/React__namespace.createElement(DeferredHydrationScript, {
+        nonce: props.nonce,
         key: i
       }));
     }
@@ -758,6 +762,7 @@ import(${JSON.stringify(manifest.entry.module)});`;
   })), !isHydrated && initialScripts, !isHydrated && deferredScripts);
 }
 function DeferredHydrationScript({
+  nonce,
   dataKey,
   deferredData,
   routeId
@@ -773,6 +778,7 @@ function DeferredHydrationScript({
     typeof document === "undefined" && deferredData && dataKey && routeId ? null : /*#__PURE__*/React__namespace.createElement("script", {
       async: true,
       suppressHydrationWarning: true,
+      nonce,
       dangerouslySetInnerHTML: {
         __html: " "
       }
@@ -780,10 +786,12 @@ function DeferredHydrationScript({
   }, typeof document === "undefined" && deferredData && dataKey && routeId ? /*#__PURE__*/React__namespace.createElement(Await, {
     resolve: deferredData.data[dataKey],
     errorElement: /*#__PURE__*/React__namespace.createElement(ErrorDeferredHydrationScript, {
+      nonce,
       dataKey: dataKey,
       routeId: routeId
     }),
     children: data => /*#__PURE__*/React__namespace.createElement("script", {
+      nonce,
       async: true,
       suppressHydrationWarning: true,
       dangerouslySetInnerHTML: {
@@ -791,6 +799,7 @@ function DeferredHydrationScript({
       }
     })
   }) : /*#__PURE__*/React__namespace.createElement("script", {
+    nonce,
     async: true,
     suppressHydrationWarning: true,
     dangerouslySetInnerHTML: {
@@ -799,6 +808,7 @@ function DeferredHydrationScript({
   }));
 }
 function ErrorDeferredHydrationScript({
+  nonce,
   dataKey,
   routeId
 }) {
@@ -811,6 +821,7 @@ function ErrorDeferredHydrationScript({
     stack: undefined
   };
   return /*#__PURE__*/React__namespace.createElement("script", {
+    nonce,
     suppressHydrationWarning: true,
     dangerouslySetInnerHTML: {
       __html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, !1, ${markup.escapeHtml(JSON.stringify(toSerialize))});`
diff --git a/node_modules/@remix-run/react/dist/esm/components.js b/node_modules/@remix-run/react/dist/esm/components.js
index f7e66b5..81686c0 100644
--- a/node_modules/@remix-run/react/dist/esm/components.js
+++ b/node_modules/@remix-run/react/dist/esm/components.js
@@ -649,6 +649,7 @@ function Scripts(props) {
         if (pendingKeys.has(key)) {
           deferredScripts.push( /*#__PURE__*/React.createElement(DeferredHydrationScript, {
             key: `${routeId} | ${key}`,
+            nonce: props.nonce,
             deferredData: deferredData,
             routeId: routeId,
             dataKey: key
@@ -681,10 +682,12 @@ window.__remixRouteModules = {${matches.map((match, index) => `${JSON.stringify(
 
 import(${JSON.stringify(manifest.entry.module)});`;
     return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("script", _extends({}, props, {
+      nonce: props.nonce,
       suppressHydrationWarning: true,
       dangerouslySetInnerHTML: createHtml(contextScript),
       type: undefined
     })), /*#__PURE__*/React.createElement("script", _extends({}, props, {
+      nonce: props.nonce,
       suppressHydrationWarning: true,
       dangerouslySetInnerHTML: createHtml(routeModulesScript),
       type: "module",
@@ -698,6 +701,7 @@ import(${JSON.stringify(manifest.entry.module)});`;
   if (!isStatic && typeof __remixContext === "object" && __remixContext.a) {
     for (let i = 0; i < __remixContext.a; i++) {
       deferredScripts.push( /*#__PURE__*/React.createElement(DeferredHydrationScript, {
+        nonce: props.nonce,
         key: i
       }));
     }
@@ -734,6 +738,7 @@ import(${JSON.stringify(manifest.entry.module)});`;
   })), !isHydrated && initialScripts, !isHydrated && deferredScripts);
 }
 function DeferredHydrationScript({
+  nonce,
   dataKey,
   deferredData,
   routeId
@@ -748,6 +753,7 @@ function DeferredHydrationScript({
     // To reproduce a hydration mismatch, just render null as a fallback.
     typeof document === "undefined" && deferredData && dataKey && routeId ? null : /*#__PURE__*/React.createElement("script", {
       async: true,
+      nonce,
       suppressHydrationWarning: true,
       dangerouslySetInnerHTML: {
         __html: " "
@@ -756,11 +762,13 @@ function DeferredHydrationScript({
   }, typeof document === "undefined" && deferredData && dataKey && routeId ? /*#__PURE__*/React.createElement(Await, {
     resolve: deferredData.data[dataKey],
     errorElement: /*#__PURE__*/React.createElement(ErrorDeferredHydrationScript, {
+      nonce,
       dataKey: dataKey,
       routeId: routeId
     }),
     children: data => /*#__PURE__*/React.createElement("script", {
       async: true,
+      nonce,
       suppressHydrationWarning: true,
       dangerouslySetInnerHTML: {
         __html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, ${escapeHtml(JSON.stringify(data))});`
@@ -768,6 +776,7 @@ function DeferredHydrationScript({
     })
   }) : /*#__PURE__*/React.createElement("script", {
     async: true,
+    nonce,
     suppressHydrationWarning: true,
     dangerouslySetInnerHTML: {
       __html: " "
@@ -775,6 +784,7 @@ function DeferredHydrationScript({
   }));
 }
 function ErrorDeferredHydrationScript({
+  nonce,
   dataKey,
   routeId
 }) {
@@ -787,6 +797,7 @@ function ErrorDeferredHydrationScript({
     stack: undefined
   };
   return /*#__PURE__*/React.createElement("script", {
+    nonce,
     suppressHydrationWarning: true,
     dangerouslySetInnerHTML: {
       __html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, !1, ${escapeHtml(JSON.stringify(toSerialize))});`
@@ -1236,6 +1247,7 @@ const LiveReload = process.env.NODE_ENV !== "development" ? () => null : functio
   let js = String.raw;
   return /*#__PURE__*/React.createElement("script", {
     nonce: nonce,
+    nonce,
     suppressHydrationWarning: true,
     dangerouslySetInnerHTML: {
       __html: js`

@kentcdodds
Copy link
Member

@jack-r-warren, I don't have time at the moment to look into why there are test failures, but if you could look into it I'm sure that would help the team prioritize this.

@brophdawg11
Copy link
Contributor

Thanks @jack-r-warren! Tested locally and this looks great 👍

@github-actions
Copy link
Contributor

🤖 Hello there,

We just published version v0.0.0-nightly-ad9adee-20230615 which includes this pull request. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

@github-actions
Copy link
Contributor

🤖 Hello there,

We just published version v0.0.0-nightly-12440f3-20230616 which includes this pull request. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

@github-actions
Copy link
Contributor

🤖 Hello there,

We just published version 1.18.0-pre.0 which includes this pull request. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

@jack-r-warren
Copy link
Contributor Author

Sorry I didn't see the comments on this, thanks so much @brophdawg11 / @kentcdodds!

@github-actions
Copy link
Contributor

🤖 Hello there,

We just published version 1.18.0 which includes this pull request. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

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

Successfully merging this pull request may close these issues.

4 participants