Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51620ac79c |
@@ -1726,7 +1726,7 @@ function codegenInstructionValue(
|
||||
}
|
||||
case 'UnaryExpression': {
|
||||
value = t.unaryExpression(
|
||||
instrValue.operator,
|
||||
instrValue.operator as 'throw', // todo
|
||||
codegenPlaceToExpression(cx, instrValue.value),
|
||||
);
|
||||
break;
|
||||
@@ -2582,16 +2582,7 @@ function codegenValue(
|
||||
value: boolean | number | string | null | undefined,
|
||||
): t.Expression {
|
||||
if (typeof value === 'number') {
|
||||
if (value < 0) {
|
||||
/**
|
||||
* Babel's code generator produces invalid JS for negative numbers when
|
||||
* run with { compact: true }.
|
||||
* See repro https://codesandbox.io/p/devbox/5d47fr
|
||||
*/
|
||||
return t.unaryExpression('-', t.numericLiteral(-value), false);
|
||||
} else {
|
||||
return t.numericLiteral(value);
|
||||
}
|
||||
return t.numericLiteral(value);
|
||||
} else if (typeof value === 'boolean') {
|
||||
return t.booleanLiteral(value);
|
||||
} else if (typeof value === 'string') {
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Repro(props) {
|
||||
const MY_CONST = -2;
|
||||
return <Stringify>{props.arg - MY_CONST}</Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Repro,
|
||||
params: [
|
||||
{
|
||||
arg: 3,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
function Repro(props) {
|
||||
const $ = _c(2);
|
||||
|
||||
const t0 = props.arg - -2;
|
||||
let t1;
|
||||
if ($[0] !== t0) {
|
||||
t1 = <Stringify>{t0}</Stringify>;
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Repro,
|
||||
params: [
|
||||
{
|
||||
arg: 3,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"children":5}</div>
|
||||
@@ -1,15 +0,0 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
function Repro(props) {
|
||||
const MY_CONST = -2;
|
||||
return <Stringify>{props.arg - MY_CONST}</Stringify>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Repro,
|
||||
params: [
|
||||
{
|
||||
arg: 3,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -242,7 +242,6 @@ export async function transformFixtureInput(
|
||||
filename: virtualFilepath,
|
||||
highlightCode: false,
|
||||
retainLines: true,
|
||||
compact: true,
|
||||
plugins: [
|
||||
[plugin, options],
|
||||
'babel-plugin-fbt',
|
||||
|
||||
@@ -12,7 +12,6 @@ import Button from './Button.js';
|
||||
import Form from './Form.js';
|
||||
import {Dynamic} from './Dynamic.js';
|
||||
import {Client} from './Client.js';
|
||||
import {Navigate} from './Navigate.js';
|
||||
|
||||
import {Note} from './cjs/Note.js';
|
||||
|
||||
@@ -90,7 +89,6 @@ export default async function App({prerender}) {
|
||||
<Note />
|
||||
<Foo>{dedupedChild}</Foo>
|
||||
<Bar>{Promise.resolve([dedupedChild])}</Bar>
|
||||
<Navigate />
|
||||
</Container>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import Container from './Container.js';
|
||||
|
||||
export function Navigate() {
|
||||
/** Repro for https://issues.chromium.org/u/1/issues/419746417 */
|
||||
function provokeChromeCrash() {
|
||||
React.startTransition(async () => {
|
||||
console.log('Default transition triggered');
|
||||
|
||||
await new Promise(resolve => {
|
||||
setTimeout(
|
||||
() => {
|
||||
history.pushState(
|
||||
{},
|
||||
'',
|
||||
`?chrome-crash-419746417=${performance.now()}`
|
||||
);
|
||||
},
|
||||
// This needs to happen before React's default transition indicator
|
||||
// is displayed but after it's scheduled.
|
||||
100 + -50
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('Default transition completed');
|
||||
resolve();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h2>Navigation fixture</h2>
|
||||
<button onClick={provokeChromeCrash}>Provoke Chrome Crash (fixed)</button>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
"clipboard-js": "^0.3.6",
|
||||
"compare-versions": "^5.0.3",
|
||||
"jsc-safe-url": "^0.2.4",
|
||||
"json5": "^2.2.3",
|
||||
"json5": "^2.1.3",
|
||||
"local-storage-fallback": "^4.1.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.23",
|
||||
"react-window": "^1.8.10"
|
||||
|
||||
@@ -135,13 +135,6 @@ const SentMarkShellTime /* */ = 0b001000000;
|
||||
const NeedUpgradeToViewTransitions /* */ = 0b010000000;
|
||||
const SentUpgradeToViewTransitions /* */ = 0b100000000;
|
||||
|
||||
type NonceOption =
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
};
|
||||
|
||||
// Per request, global state that is not contextual to the rendering subtree.
|
||||
// This cannot be resumed and therefore should only contain things that are
|
||||
// temporary working state or are never used in the prerender pass.
|
||||
@@ -154,8 +147,6 @@ export type RenderState = {
|
||||
// inline script streaming format, unused if using external runtime / data
|
||||
startInlineScript: PrecomputedChunk,
|
||||
|
||||
startInlineStyle: PrecomputedChunk,
|
||||
|
||||
// the preamble must always flush before resuming, so all these chunks must
|
||||
// be null or empty when resuming.
|
||||
|
||||
@@ -218,11 +209,6 @@ export type RenderState = {
|
||||
moduleScripts: Map<string, Resource>,
|
||||
},
|
||||
|
||||
nonce: {
|
||||
script: string | void,
|
||||
style: string | void,
|
||||
},
|
||||
|
||||
// Module-global-like reference for flushing/hoisting state of style resources
|
||||
// We need to track whether the current request has flushed any style resources
|
||||
// without sending an instruction to hoist them. we do that here
|
||||
@@ -309,8 +295,6 @@ export type ResumableState = {
|
||||
},
|
||||
};
|
||||
|
||||
let currentlyFlushingRenderState: RenderState | null = null;
|
||||
|
||||
const dataElementQuotedEnd = stringToPrecomputedChunk('"></template>');
|
||||
|
||||
const startInlineScript = stringToPrecomputedChunk('<script');
|
||||
@@ -323,8 +307,6 @@ const scriptIntegirty = stringToPrecomputedChunk(' integrity="');
|
||||
const scriptCrossOrigin = stringToPrecomputedChunk(' crossorigin="');
|
||||
const endAsyncScript = stringToPrecomputedChunk(' async=""></script>');
|
||||
|
||||
const startInlineStyle = stringToPrecomputedChunk('<style');
|
||||
|
||||
/**
|
||||
* This escaping function is designed to work with with inline scripts where the entire
|
||||
* contents are escaped. Because we know we are escaping the entire script we can avoid for instance
|
||||
@@ -383,32 +365,17 @@ if (__DEV__) {
|
||||
// is set, the server will send instructions via data attributes (instead of inline scripts)
|
||||
export function createRenderState(
|
||||
resumableState: ResumableState,
|
||||
nonce:
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
}
|
||||
| void,
|
||||
nonce: string | void,
|
||||
externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
|
||||
importMap: ImportMap | void,
|
||||
onHeaders: void | ((headers: HeadersDescriptor) => void),
|
||||
maxHeadersLength: void | number,
|
||||
): RenderState {
|
||||
const nonceScript = typeof nonce === 'string' ? nonce : nonce && nonce.script;
|
||||
const inlineScriptWithNonce =
|
||||
nonceScript === undefined
|
||||
nonce === undefined
|
||||
? startInlineScript
|
||||
: stringToPrecomputedChunk(
|
||||
'<script nonce="' + escapeTextForBrowser(nonceScript) + '"',
|
||||
);
|
||||
const nonceStyle =
|
||||
typeof nonce === 'string' ? undefined : nonce && nonce.style;
|
||||
const inlineStyleWithNonce =
|
||||
nonceStyle === undefined
|
||||
? startInlineStyle
|
||||
: stringToPrecomputedChunk(
|
||||
'<style nonce="' + escapeTextForBrowser(nonceStyle) + '"',
|
||||
'<script nonce="' + escapeTextForBrowser(nonce) + '"',
|
||||
);
|
||||
const idPrefix = resumableState.idPrefix;
|
||||
|
||||
@@ -436,7 +403,7 @@ export function createRenderState(
|
||||
src: externalRuntimeConfig,
|
||||
async: true,
|
||||
integrity: undefined,
|
||||
nonce: nonceScript,
|
||||
nonce: nonce,
|
||||
});
|
||||
} else {
|
||||
externalRuntimeScript = {
|
||||
@@ -447,7 +414,7 @@ export function createRenderState(
|
||||
src: externalRuntimeConfig.src,
|
||||
async: true,
|
||||
integrity: externalRuntimeConfig.integrity,
|
||||
nonce: nonceScript,
|
||||
nonce: nonce,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -492,7 +459,6 @@ export function createRenderState(
|
||||
segmentPrefix: stringToPrecomputedChunk(idPrefix + 'S:'),
|
||||
boundaryPrefix: stringToPrecomputedChunk(idPrefix + 'B:'),
|
||||
startInlineScript: inlineScriptWithNonce,
|
||||
startInlineStyle: inlineStyleWithNonce,
|
||||
preamble: createPreambleState(),
|
||||
|
||||
externalRuntimeScript: externalRuntimeScript,
|
||||
@@ -534,10 +500,7 @@ export function createRenderState(
|
||||
moduleScripts: new Map(),
|
||||
},
|
||||
|
||||
nonce: {
|
||||
script: nonceScript,
|
||||
style: nonceStyle,
|
||||
},
|
||||
nonce,
|
||||
// like a module global for currently rendering boundary
|
||||
hoistableState: null,
|
||||
stylesToHoist: false,
|
||||
@@ -576,10 +539,10 @@ export function createRenderState(
|
||||
stringToChunk(escapeTextForBrowser(src)),
|
||||
attributeEnd,
|
||||
);
|
||||
if (nonceScript) {
|
||||
if (nonce) {
|
||||
bootstrapChunks.push(
|
||||
scriptNonce,
|
||||
stringToChunk(escapeTextForBrowser(nonceScript)),
|
||||
stringToChunk(escapeTextForBrowser(nonce)),
|
||||
attributeEnd,
|
||||
);
|
||||
}
|
||||
@@ -608,7 +571,7 @@ export function createRenderState(
|
||||
const props: PreloadModuleProps = ({
|
||||
rel: 'modulepreload',
|
||||
fetchPriority: 'low',
|
||||
nonce: nonceScript,
|
||||
nonce,
|
||||
}: any);
|
||||
if (typeof scriptConfig === 'string') {
|
||||
props.href = src = scriptConfig;
|
||||
@@ -633,10 +596,10 @@ export function createRenderState(
|
||||
stringToChunk(escapeTextForBrowser(src)),
|
||||
attributeEnd,
|
||||
);
|
||||
if (nonceScript) {
|
||||
if (nonce) {
|
||||
bootstrapChunks.push(
|
||||
scriptNonce,
|
||||
stringToChunk(escapeTextForBrowser(nonceScript)),
|
||||
stringToChunk(escapeTextForBrowser(nonce)),
|
||||
attributeEnd,
|
||||
);
|
||||
}
|
||||
@@ -664,7 +627,7 @@ export function createRenderState(
|
||||
|
||||
export function resumeRenderState(
|
||||
resumableState: ResumableState,
|
||||
nonce: NonceOption | void,
|
||||
nonce: string | void,
|
||||
): RenderState {
|
||||
return createRenderState(
|
||||
resumableState,
|
||||
@@ -3083,7 +3046,6 @@ function pushStyle(
|
||||
}
|
||||
const precedence = props.precedence;
|
||||
const href = props.href;
|
||||
const nonce = props.nonce;
|
||||
|
||||
if (
|
||||
formatContext.insertionMode === SVG_MODE ||
|
||||
@@ -3129,33 +3091,15 @@ function pushStyle(
|
||||
styleQueue = {
|
||||
precedence: stringToChunk(escapeTextForBrowser(precedence)),
|
||||
rules: ([]: Array<Chunk | PrecomputedChunk>),
|
||||
hrefs: ([]: Array<Chunk | PrecomputedChunk>),
|
||||
hrefs: [stringToChunk(escapeTextForBrowser(href))],
|
||||
sheets: (new Map(): Map<string, StylesheetResource>),
|
||||
};
|
||||
renderState.styles.set(precedence, styleQueue);
|
||||
}
|
||||
|
||||
const nonceStyle = renderState.nonce.style;
|
||||
if (!nonceStyle || nonceStyle === nonce) {
|
||||
if (__DEV__) {
|
||||
if (!nonceStyle && nonce) {
|
||||
console.error(
|
||||
'React encountered a style tag with `precedence` "%s" and `nonce` "%s". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
|
||||
precedence,
|
||||
nonce,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We have seen this precedence before and need to track this href
|
||||
styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
|
||||
pushStyleContents(styleQueue.rules, props);
|
||||
} else if (__DEV__) {
|
||||
console.error(
|
||||
'React encountered a style tag with `precedence` "%s" and `nonce` "%s". When React manages style rules using `precedence` it will only include rules if the nonce matches the style nonce "%s" that was included with this render.',
|
||||
precedence,
|
||||
nonce,
|
||||
nonceStyle,
|
||||
);
|
||||
}
|
||||
pushStyleContents(styleQueue.rules, props);
|
||||
}
|
||||
if (styleQueue) {
|
||||
// We need to track whether this boundary should wait on this resource or not.
|
||||
@@ -5204,7 +5148,7 @@ function escapeJSObjectForInstructionScripts(input: Object): string {
|
||||
}
|
||||
|
||||
const lateStyleTagResourceOpen1 = stringToPrecomputedChunk(
|
||||
' media="not all" data-precedence="',
|
||||
'<style media="not all" data-precedence="',
|
||||
);
|
||||
const lateStyleTagResourceOpen2 = stringToPrecomputedChunk('" data-href="');
|
||||
const lateStyleTagResourceOpen3 = stringToPrecomputedChunk('">');
|
||||
@@ -5232,10 +5176,6 @@ function flushStyleTagsLateForBoundary(
|
||||
}
|
||||
let i = 0;
|
||||
if (hrefs.length) {
|
||||
writeChunk(
|
||||
this,
|
||||
((currentlyFlushingRenderState: any): RenderState).startInlineStyle,
|
||||
);
|
||||
writeChunk(this, lateStyleTagResourceOpen1);
|
||||
writeChunk(this, styleQueue.precedence);
|
||||
writeChunk(this, lateStyleTagResourceOpen2);
|
||||
@@ -5285,9 +5225,7 @@ export function writeHoistablesForBoundary(
|
||||
destinationHasCapacity = true;
|
||||
|
||||
// Flush style tags for each precedence this boundary depends on
|
||||
currentlyFlushingRenderState = renderState;
|
||||
hoistableState.styles.forEach(flushStyleTagsLateForBoundary, destination);
|
||||
currentlyFlushingRenderState = null;
|
||||
|
||||
// Determine if this boundary has stylesheets that need to be awaited upon completion
|
||||
hoistableState.stylesheets.forEach(hasStylesToHoist);
|
||||
@@ -5330,7 +5268,9 @@ function flushStyleInPreamble(
|
||||
stylesheet.state = PREAMBLE;
|
||||
}
|
||||
|
||||
const styleTagResourceOpen1 = stringToPrecomputedChunk(' data-precedence="');
|
||||
const styleTagResourceOpen1 = stringToPrecomputedChunk(
|
||||
'<style data-precedence="',
|
||||
);
|
||||
const styleTagResourceOpen2 = stringToPrecomputedChunk('" data-href="');
|
||||
const spaceSeparator = stringToPrecomputedChunk(' ');
|
||||
const styleTagResourceOpen3 = stringToPrecomputedChunk('">');
|
||||
@@ -5352,10 +5292,6 @@ function flushStylesInPreamble(
|
||||
// order so even if there are no rules for style tags at this precedence we emit an empty style
|
||||
// tag with the data-precedence attribute
|
||||
if (!hasStylesheets || hrefs.length) {
|
||||
writeChunk(
|
||||
this,
|
||||
((currentlyFlushingRenderState: any): RenderState).startInlineStyle,
|
||||
);
|
||||
writeChunk(this, styleTagResourceOpen1);
|
||||
writeChunk(this, styleQueue.precedence);
|
||||
let i = 0;
|
||||
@@ -5530,9 +5466,7 @@ export function writePreambleStart(
|
||||
renderState.highImagePreloads.clear();
|
||||
|
||||
// Flush unblocked stylesheets by precedence
|
||||
currentlyFlushingRenderState = renderState;
|
||||
renderState.styles.forEach(flushStylesInPreamble, destination);
|
||||
currentlyFlushingRenderState = null;
|
||||
|
||||
const importMapChunks = renderState.importMapChunks;
|
||||
for (i = 0; i < importMapChunks.length; i++) {
|
||||
|
||||
@@ -47,7 +47,6 @@ export type RenderState = {
|
||||
segmentPrefix: PrecomputedChunk,
|
||||
boundaryPrefix: PrecomputedChunk,
|
||||
startInlineScript: PrecomputedChunk,
|
||||
startInlineStyle: PrecomputedChunk,
|
||||
preamble: PreambleState,
|
||||
externalRuntimeScript: null | any,
|
||||
bootstrapChunks: Array<Chunk | PrecomputedChunk>,
|
||||
@@ -77,10 +76,6 @@ export type RenderState = {
|
||||
scripts: Map<string, Resource>,
|
||||
moduleScripts: Map<string, Resource>,
|
||||
},
|
||||
nonce: {
|
||||
script: string | void,
|
||||
style: string | void,
|
||||
},
|
||||
stylesToHoist: boolean,
|
||||
// This is an extra field for the legacy renderer
|
||||
generateStaticMarkup: boolean,
|
||||
@@ -104,7 +99,6 @@ export function createRenderState(
|
||||
segmentPrefix: renderState.segmentPrefix,
|
||||
boundaryPrefix: renderState.boundaryPrefix,
|
||||
startInlineScript: renderState.startInlineScript,
|
||||
startInlineStyle: renderState.startInlineStyle,
|
||||
preamble: renderState.preamble,
|
||||
externalRuntimeScript: renderState.externalRuntimeScript,
|
||||
bootstrapChunks: renderState.bootstrapChunks,
|
||||
@@ -124,7 +118,6 @@ export function createRenderState(
|
||||
scripts: renderState.scripts,
|
||||
bulkPreloads: renderState.bulkPreloads,
|
||||
preloads: renderState.preloads,
|
||||
nonce: renderState.nonce,
|
||||
stylesToHoist: renderState.stylesToHoist,
|
||||
|
||||
// This is an extra field for the legacy renderer
|
||||
|
||||
@@ -8,7 +8,7 @@ export const clientRenderBoundary =
|
||||
export const completeBoundary =
|
||||
'$RB=[];$RV=function(c){$RT=performance.now();for(var a=0;a<c.length;a+=2){var b=c[a],h=c[a+1],e=b.parentNode;if(e){var f=b.previousSibling,g=0;do{if(b&&8===b.nodeType){var d=b.data;if("/$"===d||"/&"===d)if(0===g)break;else g--;else"$"!==d&&"$?"!==d&&"$~"!==d&&"$!"!==d&&"&"!==d||g++}d=b.nextSibling;e.removeChild(b);b=d}while(b);for(;h.firstChild;)e.insertBefore(h.firstChild,b);f.data="$";f._reactRetry&&f._reactRetry()}}c.length=0};$RC=function(c,a){if(a=document.getElementById(a))if(a.parentNode.removeChild(a),c=document.getElementById(c))c.previousSibling.data="$~",$RB.push(c,a),2===$RB.length&&setTimeout($RV.bind(null,$RB),("number"!==typeof $RT?0:$RT)+300-performance.now())};';
|
||||
export const completeBoundaryUpgradeToViewTransitions =
|
||||
'$RV=function(w,f){function h(a,d){var k=a.getAttribute(d);k&&(d=a.style,l.push(a,d.viewTransitionName,d.viewTransitionClass),"auto"!==k&&(d.viewTransitionClass=k),(a=a.getAttribute("vt-name"))||(a="\\u00abT"+F++ +"\\u00bb"),d.viewTransitionName=a,x=!0)}var x=!1,F=0,l=[];try{var e=document.__reactViewTransition;if(e){e.finished.finally($RV.bind(null,f));return}var m=new Map;for(e=1;e<f.length;e+=2)for(var g=f[e].querySelectorAll("[vt-share]"),c=0;c<g.length;c++){var b=g[c];m.set(b.getAttribute("vt-name"),b)}for(g=0;g<f.length;g+=2){var y=f[g],t=y.parentNode;if(t){var r=t.getBoundingClientRect();if(r.left||r.top||r.width||r.height){b=y;for(e=0;b;){if(8===b.nodeType){var p=b.data;if("/$"===p)if(0===e)break;else e--;else"$"!==p&&"$?"!==p&&"$~"!==p&&"$!"!==p||e++}else if(1===b.nodeType){c=b;var z=c.getAttribute("vt-name"),u=m.get(z);h(c,u?"vt-share":"vt-exit");u&&(h(u,"vt-share"),m.set(z,null));var A=c.querySelectorAll("[vt-share]");for(c=0;c<A.length;c++){var B=A[c],C=B.getAttribute("vt-name"),D=m.get(C);\nD&&(h(B,"vt-share"),h(D,"vt-share"),m.set(C,null))}}b=b.nextSibling}for(var q=f[g+1].firstElementChild;q;)null!==m.get(q.getAttribute("vt-name"))&&h(q,"vt-enter"),q=q.nextElementSibling;b=t;do for(var n=b.firstElementChild;n;){var E=n.getAttribute("vt-update");E&&"none"!==E&&!l.includes(n)&&h(n,"vt-update");n=n.nextElementSibling}while((b=b.parentNode)&&1===b.nodeType&&"none"!==b.getAttribute("vt-update"))}}}if(x){var v=document.__reactViewTransition=document.startViewTransition({update:function(){w(f,\ndocument.documentElement.clientHeight);return Promise.race([document.fonts.ready,new Promise(function(a){return setTimeout(a,500)})])},types:[]});v.ready.finally(function(){for(var a=l.length-3;0<=a;a-=3){var d=l[a],k=d.style;k.viewTransitionName=l[a+1];k.viewTransitionClass=l[a+1];""===d.getAttribute("style")&&d.removeAttribute("style")}});v.finished.finally(function(){document.__reactViewTransition===v&&(document.__reactViewTransition=null)});$RB=[];return}}catch(a){}w(f)}.bind(null,$RV);';
|
||||
'$RV=function(w,f){function h(b,d){var k=b.getAttribute(d);k&&(d=b.style,l.push(b,d.viewTransitionName,d.viewTransitionClass),"auto"!==k&&(d.viewTransitionClass=k),(b=b.getAttribute("vt-name"))||(b="\\u00abT"+F++ +"\\u00bb"),d.viewTransitionName=b,x=!0)}var x=!1,F=0,l=[];try{var e=document.__reactViewTransition;if(e){e.finished.finally($RV.bind(null,f));return}var m=new Map;for(e=1;e<f.length;e+=2)for(var g=f[e].querySelectorAll("[vt-share]"),c=0;c<g.length;c++){var a=g[c];m.set(a.getAttribute("vt-name"),a)}for(g=0;g<f.length;g+=2){var y=f[g],t=y.parentNode;if(t){var r=t.getBoundingClientRect();if(r.left||r.top||r.width||r.height){a=y;for(e=0;a;){if(8===a.nodeType){var p=a.data;if("/$"===p)if(0===e)break;else e--;else"$"!==p&&"$?"!==p&&"$~"!==p&&"$!"!==p||e++}else if(1===a.nodeType){c=a;var z=c.getAttribute("vt-name"),u=m.get(z);h(c,u?"vt-share":"vt-exit");u&&(h(u,"vt-share"),m.set(z,null));var A=c.querySelectorAll("[vt-share]");for(c=0;c<A.length;c++){var B=A[c],C=B.getAttribute("vt-name"),D=m.get(C);\nD&&(h(B,"vt-share"),h(D,"vt-share"),m.set(C,null))}}a=a.nextSibling}for(var q=f[g+1].firstElementChild;q;)null!==m.get(q.getAttribute("vt-name"))&&h(q,"vt-enter"),q=q.nextElementSibling;a=t;do for(var n=a.firstElementChild;n;){var E=n.getAttribute("vt-update");E&&"none"!==E&&!l.includes(n)&&h(n,"vt-update");n=n.nextElementSibling}while((a=a.parentNode)&&1===a.nodeType&&"none"!==a.getAttribute("vt-update"))}}}if(x){var v=document.__reactViewTransition=document.startViewTransition({update:w.bind(null,\nf),types:[]});v.ready.finally(function(){for(var b=l.length-3;0<=b;b-=3){var d=l[b],k=d.style;k.viewTransitionName=l[b+1];k.viewTransitionClass=l[b+1];""===d.getAttribute("style")&&d.removeAttribute("style")}});v.finished.finally(function(){document.__reactViewTransition===v&&(document.__reactViewTransition=null)});$RB=[];return}}catch(b){}w(f)}.bind(null,$RV);';
|
||||
export const completeBoundaryWithStyles =
|
||||
'$RM=new Map;$RR=function(n,w,p){function u(q){this._p=null;q()}for(var r=new Map,t=document,h,b,e=t.querySelectorAll("link[data-precedence],style[data-precedence]"),v=[],k=0;b=e[k++];)"not all"===b.getAttribute("media")?v.push(b):("LINK"===b.tagName&&$RM.set(b.getAttribute("href"),b),r.set(b.dataset.precedence,h=b));e=0;b=[];var l,a;for(k=!0;;){if(k){var f=p[e++];if(!f){k=!1;e=0;continue}var c=!1,m=0;var d=f[m++];if(a=$RM.get(d)){var g=a._p;c=!0}else{a=t.createElement("link");a.href=d;a.rel=\n"stylesheet";for(a.dataset.precedence=l=f[m++];g=f[m++];)a.setAttribute(g,f[m++]);g=a._p=new Promise(function(q,x){a.onload=u.bind(a,q);a.onerror=u.bind(a,x)});$RM.set(d,a)}d=a.getAttribute("media");!g||d&&!matchMedia(d).matches||b.push(g);if(c)continue}else{a=v[e++];if(!a)break;l=a.getAttribute("data-precedence");a.removeAttribute("media")}c=r.get(l)||h;c===h&&(h=a);r.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=t.head,c.insertBefore(a,c.firstChild))}if(p=document.getElementById(n))p.previousSibling.data=\n"$~";Promise.all(b).then($RC.bind(null,n,w),$RX.bind(null,n,"CSS failed to load"))};';
|
||||
export const completeSegment =
|
||||
|
||||
@@ -13,8 +13,6 @@ const SUSPENSE_PENDING_START_DATA = '$?';
|
||||
const SUSPENSE_QUEUED_START_DATA = '$~';
|
||||
const SUSPENSE_FALLBACK_START_DATA = '$!';
|
||||
|
||||
const SUSPENSEY_FONT_TIMEOUT = 500;
|
||||
|
||||
// TODO: Symbols that are referenced outside this module use dynamic accessor
|
||||
// notation instead of dot notation to prevent Closure's advanced compilation
|
||||
// mode from renaming. We could use extern files instead, but I couldn't get it
|
||||
@@ -253,18 +251,7 @@ export function revealCompletedBoundariesWithViewTransitions(
|
||||
const transition = (document['__reactViewTransition'] = document[
|
||||
'startViewTransition'
|
||||
]({
|
||||
update: () => {
|
||||
revealBoundaries(
|
||||
batch,
|
||||
// Force layout to trigger font loading, we pass the actual value to trick minifiers.
|
||||
document.documentElement.clientHeight,
|
||||
);
|
||||
return Promise.race([
|
||||
// Block on fonts finishing loading before revealing these boundaries.
|
||||
document.fonts.ready,
|
||||
new Promise(resolve => setTimeout(resolve, SUSPENSEY_FONT_TIMEOUT)),
|
||||
]);
|
||||
},
|
||||
update: revealBoundaries.bind(null, batch),
|
||||
types: [], // TODO: Add a hard coded type for Suspense reveals.
|
||||
}));
|
||||
transition.ready.finally(() => {
|
||||
|
||||
@@ -10279,172 +10279,4 @@ describe('ReactDOMFizzServer', () => {
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
|
||||
it('can render styles with nonce', async () => {
|
||||
CSPnonce = 'R4nd0m';
|
||||
await act(() => {
|
||||
const {pipe} = renderToPipeableStream(
|
||||
<>
|
||||
<style
|
||||
href="foo"
|
||||
precedence="default"
|
||||
nonce={CSPnonce}>{`.foo { color: hotpink; }`}</style>
|
||||
<style
|
||||
href="bar"
|
||||
precedence="default"
|
||||
nonce={CSPnonce}>{`.bar { background-color: blue; }`}</style>
|
||||
</>,
|
||||
{nonce: {style: CSPnonce}},
|
||||
);
|
||||
pipe(writable);
|
||||
});
|
||||
expect(document.querySelector('style').nonce).toBe(CSPnonce);
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div id="container">
|
||||
<style
|
||||
data-precedence="default"
|
||||
data-href="foo bar"
|
||||
nonce={
|
||||
CSPnonce
|
||||
}>{`.foo { color: hotpink; }.bar { background-color: blue; }`}</style>
|
||||
</div>
|
||||
</body>
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
|
||||
it("shouldn't render styles with mismatched nonce", async () => {
|
||||
CSPnonce = 'R4nd0m';
|
||||
await act(() => {
|
||||
const {pipe} = renderToPipeableStream(
|
||||
<>
|
||||
<style
|
||||
href="foo"
|
||||
precedence="default"
|
||||
nonce={CSPnonce}>{`.foo { color: hotpink; }`}</style>
|
||||
<style
|
||||
href="bar"
|
||||
precedence="default"
|
||||
nonce={`${CSPnonce}${CSPnonce}`}>{`.bar { background-color: blue; }`}</style>
|
||||
</>,
|
||||
{nonce: {style: CSPnonce}},
|
||||
);
|
||||
pipe(writable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0mR4nd0m". When React manages style rules using `precedence` it will only include rules if the nonce matches the style nonce "R4nd0m" that was included with this render.',
|
||||
]);
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div id="container">
|
||||
<style
|
||||
data-precedence="default"
|
||||
data-href="foo"
|
||||
nonce={CSPnonce}>{`.foo { color: hotpink; }`}</style>
|
||||
</div>
|
||||
</body>
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
|
||||
it("should render styles without nonce when render call doesn't receive nonce", async () => {
|
||||
await act(() => {
|
||||
const {pipe} = renderToPipeableStream(
|
||||
<>
|
||||
<style
|
||||
href="foo"
|
||||
precedence="default"
|
||||
nonce="R4nd0m">{`.foo { color: hotpink; }`}</style>
|
||||
</>,
|
||||
);
|
||||
pipe(writable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0m". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
|
||||
]);
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div id="container">
|
||||
<style
|
||||
data-precedence="default"
|
||||
data-href="foo">{`.foo { color: hotpink; }`}</style>
|
||||
</div>
|
||||
</body>
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render styles without nonce when render call receives a string nonce dedicated to scripts', async () => {
|
||||
CSPnonce = 'R4nd0m';
|
||||
await act(() => {
|
||||
const {pipe} = renderToPipeableStream(
|
||||
<>
|
||||
<style
|
||||
href="foo"
|
||||
precedence="default"
|
||||
nonce={CSPnonce}>{`.foo { color: hotpink; }`}</style>
|
||||
</>,
|
||||
{nonce: CSPnonce},
|
||||
);
|
||||
pipe(writable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0m". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
|
||||
]);
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div id="container">
|
||||
<style
|
||||
data-precedence="default"
|
||||
data-href="foo">{`.foo { color: hotpink; }`}</style>
|
||||
</div>
|
||||
</body>
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow for different script and style nonces', async () => {
|
||||
CSPnonce = 'R4nd0m';
|
||||
await act(() => {
|
||||
const {pipe} = renderToPipeableStream(
|
||||
<>
|
||||
<style
|
||||
href="foo"
|
||||
precedence="default"
|
||||
nonce="D1ff3r3nt">{`.foo { color: hotpink; }`}</style>
|
||||
</>,
|
||||
{
|
||||
nonce: {script: CSPnonce, style: 'D1ff3r3nt'},
|
||||
bootstrapScriptContent: 'function noop(){}',
|
||||
},
|
||||
);
|
||||
pipe(writable);
|
||||
});
|
||||
const scripts = Array.from(container.getElementsByTagName('script')).filter(
|
||||
node => node.getAttribute('nonce') === CSPnonce,
|
||||
);
|
||||
expect(scripts[scripts.length - 1].textContent).toBe('function noop(){}');
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div id="container">
|
||||
<style
|
||||
data-precedence="default"
|
||||
data-href="foo"
|
||||
nonce="D1ff3r3nt">{`.foo { color: hotpink; }`}</style>
|
||||
</div>
|
||||
</body>
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8595,86 +8595,6 @@ background-color: green;
|
||||
' in style (at **)',
|
||||
]);
|
||||
});
|
||||
|
||||
it('can emit styles with nonce', async () => {
|
||||
const nonce = 'R4nD0m';
|
||||
const fooCss = '.foo { color: hotpink; }';
|
||||
const barCss = '.bar { background-color: blue; }';
|
||||
const bazCss = '.baz { border: 1px solid black; }';
|
||||
await act(() => {
|
||||
renderToPipeableStream(
|
||||
<html>
|
||||
<body>
|
||||
<Suspense>
|
||||
<BlockedOn value="first">
|
||||
<div>first</div>
|
||||
<style href="foo" precedence="default" nonce={nonce}>
|
||||
{fooCss}
|
||||
</style>
|
||||
<style href="bar" precedence="default" nonce={nonce}>
|
||||
{barCss}
|
||||
</style>
|
||||
<BlockedOn value="second">
|
||||
<div>second</div>
|
||||
<style href="baz" precedence="default" nonce={nonce}>
|
||||
{bazCss}
|
||||
</style>
|
||||
</BlockedOn>
|
||||
</BlockedOn>
|
||||
</Suspense>
|
||||
</body>
|
||||
</html>,
|
||||
{nonce: {style: nonce}},
|
||||
).pipe(writable);
|
||||
});
|
||||
|
||||
expect(getMeaningfulChildren(document)).toEqual(
|
||||
<html>
|
||||
<head />
|
||||
<body />
|
||||
</html>,
|
||||
);
|
||||
|
||||
await act(() => {
|
||||
resolveText('first');
|
||||
});
|
||||
|
||||
expect(getMeaningfulChildren(document)).toEqual(
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<style
|
||||
data-href="foo bar"
|
||||
data-precedence="default"
|
||||
media="not all"
|
||||
nonce={nonce}>
|
||||
{`${fooCss}${barCss}`}
|
||||
</style>
|
||||
</body>
|
||||
</html>,
|
||||
);
|
||||
|
||||
await act(() => {
|
||||
resolveText('second');
|
||||
});
|
||||
|
||||
expect(getMeaningfulChildren(document)).toEqual(
|
||||
<html>
|
||||
<head>
|
||||
<style data-href="foo bar" data-precedence="default" nonce={nonce}>
|
||||
{`${fooCss}${barCss}`}
|
||||
</style>
|
||||
<style data-href="baz" data-precedence="default" nonce={nonce}>
|
||||
{bazCss}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>first</div>
|
||||
<div>second</div>
|
||||
</body>
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Script Resources', () => {
|
||||
|
||||
@@ -38,8 +38,7 @@ export function defaultOnDefaultTransitionIndicator(): void | (() => void) {
|
||||
if (!isCancelled) {
|
||||
// Some other navigation completed but we should still be running.
|
||||
// Start another fake one to keep the loading indicator going.
|
||||
// There needs to be an async gap to work around https://issues.chromium.org/u/1/issues/419746417.
|
||||
setTimeout(startFakeNavigation, 20);
|
||||
startFakeNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +70,7 @@ export function defaultOnDefaultTransitionIndicator(): void | (() => void) {
|
||||
}
|
||||
}
|
||||
|
||||
// Delay the start a bit in case this is a fast Transition.
|
||||
// Delay the start a bit in case this is a fast navigation.
|
||||
setTimeout(startFakeNavigation, 100);
|
||||
|
||||
return function () {
|
||||
|
||||
@@ -40,17 +40,10 @@ import {
|
||||
import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
|
||||
ensureCorrectIsomorphicReactVersion();
|
||||
|
||||
type NonceOption =
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
};
|
||||
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
namespaceURI?: string,
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
bootstrapScriptContent?: string,
|
||||
bootstrapScripts?: Array<string | BootstrapScriptDescriptor>,
|
||||
bootstrapModules?: Array<string | BootstrapScriptDescriptor>,
|
||||
@@ -66,7 +59,7 @@ type Options = {
|
||||
};
|
||||
|
||||
type ResumeOptions = {
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
signal?: AbortSignal,
|
||||
onError?: (error: mixed) => ?string,
|
||||
onPostpone?: (reason: string) => void,
|
||||
|
||||
@@ -37,12 +37,7 @@ ensureCorrectIsomorphicReactVersion();
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
namespaceURI?: string,
|
||||
nonce?:
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
},
|
||||
nonce?: string,
|
||||
bootstrapScriptContent?: string,
|
||||
bootstrapScripts?: Array<string | BootstrapScriptDescriptor>,
|
||||
bootstrapModules?: Array<string | BootstrapScriptDescriptor>,
|
||||
|
||||
@@ -40,17 +40,10 @@ import {
|
||||
import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
|
||||
ensureCorrectIsomorphicReactVersion();
|
||||
|
||||
type NonceOption =
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
};
|
||||
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
namespaceURI?: string,
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
bootstrapScriptContent?: string,
|
||||
bootstrapScripts?: Array<string | BootstrapScriptDescriptor>,
|
||||
bootstrapModules?: Array<string | BootstrapScriptDescriptor>,
|
||||
@@ -66,7 +59,7 @@ type Options = {
|
||||
};
|
||||
|
||||
type ResumeOptions = {
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
signal?: AbortSignal,
|
||||
onError?: (error: mixed) => ?string,
|
||||
onPostpone?: (reason: string) => void,
|
||||
|
||||
@@ -56,17 +56,10 @@ function createCancelHandler(request: Request, reason: string) {
|
||||
};
|
||||
}
|
||||
|
||||
type NonceOption =
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
};
|
||||
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
namespaceURI?: string,
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
bootstrapScriptContent?: string,
|
||||
bootstrapScripts?: Array<string | BootstrapScriptDescriptor>,
|
||||
bootstrapModules?: Array<string | BootstrapScriptDescriptor>,
|
||||
@@ -84,7 +77,7 @@ type Options = {
|
||||
};
|
||||
|
||||
type ResumeOptions = {
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
onShellReady?: () => void,
|
||||
onShellError?: (error: mixed) => void,
|
||||
onAllReady?: () => void,
|
||||
|
||||
@@ -43,13 +43,6 @@ import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
|
||||
import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
|
||||
ensureCorrectIsomorphicReactVersion();
|
||||
|
||||
type NonceOption =
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
};
|
||||
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
namespaceURI?: string,
|
||||
@@ -158,7 +151,7 @@ function prerender(
|
||||
}
|
||||
|
||||
type ResumeOptions = {
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
signal?: AbortSignal,
|
||||
onError?: (error: mixed) => ?string,
|
||||
onPostpone?: (reason: string) => void,
|
||||
|
||||
@@ -43,13 +43,6 @@ import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
|
||||
import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
|
||||
ensureCorrectIsomorphicReactVersion();
|
||||
|
||||
type NonceOption =
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
};
|
||||
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
namespaceURI?: string,
|
||||
@@ -157,7 +150,7 @@ function prerender(
|
||||
}
|
||||
|
||||
type ResumeOptions = {
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
signal?: AbortSignal,
|
||||
onError?: (error: mixed) => ?string,
|
||||
onPostpone?: (reason: string) => void,
|
||||
|
||||
@@ -44,13 +44,6 @@ import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
|
||||
import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
|
||||
ensureCorrectIsomorphicReactVersion();
|
||||
|
||||
type NonceOption =
|
||||
| string
|
||||
| {
|
||||
script?: string,
|
||||
style?: string,
|
||||
};
|
||||
|
||||
type Options = {
|
||||
identifierPrefix?: string,
|
||||
namespaceURI?: string,
|
||||
@@ -158,7 +151,7 @@ function prerenderToNodeStream(
|
||||
}
|
||||
|
||||
type ResumeOptions = {
|
||||
nonce?: NonceOption,
|
||||
nonce?: string,
|
||||
signal?: AbortSignal,
|
||||
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
|
||||
onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
|
||||
|
||||
@@ -194,7 +194,7 @@ export const disableLegacyContext = true;
|
||||
export const disableLegacyContextForFunctionComponents = true;
|
||||
|
||||
// Enable the moveBefore() alternative to insertBefore(). This preserves states of moves.
|
||||
export const enableMoveBefore = false;
|
||||
export const enableMoveBefore = __EXPERIMENTAL__;
|
||||
|
||||
// Disabled caching behavior of `react/cache` in client runtimes.
|
||||
export const disableClientCache = true;
|
||||
|
||||
@@ -11773,7 +11773,7 @@ json5@^2.1.0:
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
json5@^2.1.2:
|
||||
json5@^2.1.2, json5@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
|
||||
integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
|
||||
|
||||
Reference in New Issue
Block a user