2642
noscriptTagInScope: boolean,
2643
): null {
2644
if (enableFloat) {
2645
+ const asyncProp = props.async;
2646
if (
2647
+ typeof props.src !== 'string' ||
2648
+ !props.src ||
2649
+ !(
2650
+ asyncProp &&
2651
+ typeof asyncProp !== 'function' &&
2652
+ typeof asyncProp !== 'symbol'
2653
+ ) ||
2654
+ props.onLoad ||
2655
+ props.onError ||
2656
insertionMode === SVG_MODE ||
2657
noscriptTagInScope ||
2648
- props.itemProp != null ||
2649
- typeof props.src !== 'string' ||
2650
- !props.src
2658
+ props.itemProp != null
2659
) {
2652
- // This script will not be a resource nor can it be preloaded, we bailout early
2653
- // and emit it in place.
2660
+ // This script will not be a resource, we bailout early and emit it in place.
2661
return pushScriptImpl(target, props);
2662
}
2663
2664
const src = props.src;
2665
const key = getResourceKey('script', src);
2659
- if (props.async !== true || props.onLoad || props.onError) {
2660
- // we don't want to preload nomodule scripts
2661
- if (props.noModule !== true) {
2662
- // We can't resourcify scripts with load listeners. To avoid ambiguity with
2663
- // other Resourcified async scripts on the server we omit them from the server
2664
- // stream and expect them to be inserted during hydration on the client.
2665
- // We can still preload them however so the client can start fetching the script
2666
- // as soon as possible
2667
- let resource = resources.preloadsMap.get(key);
2668
- if (!resource) {
2669
- resource = {
2670
- type: 'preload',
2671
- chunks: [],
2672
- state: NoState,
2673
- props: preloadAsScriptPropsFromProps(props.src, props),
2674
- };
2675
- resources.preloadsMap.set(key, resource);
2676
- if (__DEV__) {
2677
- markAsImplicitResourceDEV(resource, props, resource.props);
2666
+ // We can make this <script> into a ScriptResource
2667
+ let resource = resources.scriptsMap.get(key);
2668
+ if (__DEV__) {
2669
+ const devResource = getAsResourceDEV(resource);
2670
+ if (devResource) {
2671
+ switch (devResource.__provenance) {
2672
+ case 'rendered': {
2673
+ const differenceDescription = describeDifferencesForScripts(
2674
+ // Diff the props from the JSX element, not the derived resource props
2675
+ props,
2676
+ devResource.__originalProps,
2677
+ );
2678
+ if (differenceDescription) {
2679
+ console.error(
2680
+ 'React encountered a <script async={true} src="%s" .../> that has props that conflict' +
2681
+ ' with another hoistable script with the same `src`. When rendering hoistable scripts (async scripts without any loading handlers)' +
2682
+ ' the props from the first encountered instance will be used and props from later instances will be ignored.' +
2683
+ ' Update the props on both <script async={true} .../> instance so they agree.%s',
2684
+ src,
2685
+ differenceDescription,
2686
+ );
2687
+ }
2688
+ break;
2689
}
2679
- resources.usedScripts.add(resource);
2680
- pushLinkImpl(resource.chunks, resource.props);
2681
- }
2682
- }
2683
-
2684
- if (props.async !== true) {
2685
- // This is not an async script, we can preloaded it but it still needs to
2686
- // be emitted in place since it needs to hydrate on the client
2687
- pushScriptImpl(target, props);
2688
- return null;
2689
- }
2690
- } else {
2691
- // We can make this <script> into a ScriptResource
2692
- let resource = resources.scriptsMap.get(key);
2693
- if (__DEV__) {
2694
- const devResource = getAsResourceDEV(resource);
2695
- if (devResource) {
2696
- switch (devResource.__provenance) {
2697
- case 'rendered': {
2698
- const differenceDescription = describeDifferencesForScripts(
2690
+ case 'preinit': {
2691
+ const differenceDescription =
2692
+ describeDifferencesForScriptOverPreinit(
2693
// Diff the props from the JSX element, not the derived resource props
2694
props,
2701
- devResource.__originalProps,
2695
+ devResource.__propsEquivalent,
2696
+ );
2697
+ if (differenceDescription) {
2698
+ console.error(
2699
+ 'React encountered a <script async={true} src="%s" .../> with props that conflict' +
2700
+ ' with the options provided to `ReactDOM.preinit("%s", { as: "script", ... })`. React will use the first props or preinitialization' +
2701
+ ' options encountered when rendering a hoistable script with a particular `src` and will ignore any newer props or' +
2702
+ ' options. The first instance of this script resource was created using the `ReactDOM.preinit()` function.' +
2703
+ ' Please note, `ReactDOM.preinit()` is modeled off of module import assertions capabilities and does not support' +
2704
+ ' arbitrary props. If you need to have props not included with the preinit options you will need to rely on rendering' +
2705
+ ' <script> tags only.%s',
2706
+ src,
2707
+ src,
2708
+ differenceDescription,
2709
);
2703
- if (differenceDescription) {
2704
- console.error(
2705
- 'React encountered a <script async={true} src="%s" .../> that has props that conflict' +
2706
- ' with another hoistable script with the same `src`. When rendering hoistable scripts (async scripts without any loading handlers)' +
2707
- ' the props from the first encountered instance will be used and props from later instances will be ignored.' +
2708
- ' Update the props on both <script async={true} .../> instance so they agree.%s',
2709
- src,
2710
- differenceDescription,
2711
- );
2712
- }
2713
- break;
2714
- }
2715
- case 'preinit': {
2716
- const differenceDescription =
2717
- describeDifferencesForScriptOverPreinit(
2718
- // Diff the props from the JSX element, not the derived resource props
2719
- props,
2720
- devResource.__propsEquivalent,
2721
- );
2722
- if (differenceDescription) {
2723
- console.error(
2724
- 'React encountered a <script async={true} src="%s" .../> with props that conflict' +
2725
- ' with the options provided to `ReactDOM.preinit("%s", { as: "script", ... })`. React will use the first props or preinitialization' +
2726
- ' options encountered when rendering a hoistable script with a particular `src` and will ignore any newer props or' +
2727
- ' options. The first instance of this script resource was created using the `ReactDOM.preinit()` function.' +
2728
- ' Please note, `ReactDOM.preinit()` is modeled off of module import assertions capabilities and does not support' +
2729
- ' arbitrary props. If you need to have props not included with the preinit options you will need to rely on rendering' +
2730
- ' <script> tags only.%s',
2731
- src,
2732
- src,
2733
- differenceDescription,
2734
- );
2735
- }
2736
- break;
2710
}
2711
+ break;
2712
}
2713
}
2714
}
2741
- if (!resource) {
2742
- resource = {
2743
- type: 'script',
2744
- chunks: [],
2745
- state: NoState,
2746
- props: null,
2747
- };
2748
- resources.scriptsMap.set(key, resource);
2749
- if (__DEV__) {
2750
- markAsRenderedResourceDEV(resource, props);
2751
- }
2752
- // Add to the script flushing queue
2753
- resources.scripts.add(resource);
2754
-
2755
- let scriptProps = props;
2756
- const preloadResource = resources.preloadsMap.get(key);
2757
- if (preloadResource) {
2758
- // If we already had a preload we don't want that resource to flush directly.
2759
- // We let the newly created resource govern flushing.
2760
- preloadResource.state |= Blocked;
2761
- scriptProps = {...props};
2762
- adoptPreloadPropsForScriptProps(scriptProps, preloadResource.props);
2763
- }
2764
- // encode the tag as Chunks
2765
- pushScriptImpl(resource.chunks, scriptProps);
2715
+ }
2716
+ if (!resource) {
2717
+ resource = {
2718
+ type: 'script',
2719
+ chunks: [],
2720
+ state: NoState,
2721
+ props: null,
2722
+ };
2723
+ resources.scriptsMap.set(key, resource);
2724
+ if (__DEV__) {
2725
+ markAsRenderedResourceDEV(resource, props);
2726
+ }
2727
+ // Add to the script flushing queue
2728
+ resources.scripts.add(resource);
2729
+
2730
+ let scriptProps = props;
2731
+ const preloadResource = resources.preloadsMap.get(key);
2732
+ if (preloadResource) {
2733
+ // If we already had a preload we don't want that resource to flush directly.
2734
+ // We let the newly created resource govern flushing.
2735
+ preloadResource.state |= Blocked;
2736
+ scriptProps = {...props};
2737
+ adoptPreloadPropsForScriptProps(scriptProps, preloadResource.props);
2738
}
2739
+ // encode the tag as Chunks
2740
+ pushScriptImpl(resource.chunks, scriptProps);
2741
}
2742
2743
if (textEmbedded) {
4213
resources.scripts.forEach(flushResourceInPreamble, destination);
4214
resources.scripts.clear();
4215
4242
- resources.usedScripts.forEach(flushResourceInPreamble, destination);
4243
- resources.usedScripts.clear();
4244
-
4216
resources.explicitStylesheetPreloads.forEach(
4217
flushResourceInPreamble,
4218
destination,
4290
resources.scripts.forEach(flushResourceLate, destination);
4291
resources.scripts.clear();
4292
4322
- resources.usedScripts.forEach(flushResourceLate, destination);
4323
- resources.usedScripts.clear();
4324
-
4293
resources.explicitStylesheetPreloads.forEach(flushResourceLate, destination);
4294
resources.explicitStylesheetPreloads.clear();
4295
4841
precedences: Map<string, Set<StyleResource>>,
4842
stylePrecedences: Map<string, StyleTagResource>,
4843
scripts: Set<ScriptResource>,
4876
- usedScripts: Set<PreloadResource>,
4844
explicitStylesheetPreloads: Set<PreloadResource>,
4845
// explicitImagePreloads: Set<PreloadResource>,
4846
explicitScriptPreloads: Set<PreloadResource>,
4867
precedences: new Map(),
4868
stylePrecedences: new Map(),
4869
scripts: new Set(),
4903
- usedScripts: new Set(),
4870
explicitStylesheetPreloads: new Set(),
4871
// explicitImagePreloads: new Set(),
4872
explicitScriptPreloads: new Set(),
5529
};
5530
}
5531
5566
-function preloadAsScriptPropsFromProps(href: string, props: any): PreloadProps {
5567
- return {
5568
- rel: 'preload',
5569
- as: 'script',
5570
- href,
5571
- crossOrigin: props.crossOrigin,
5572
- fetchPriority: props.fetchPriority,
5573
- integrity: props.integrity,
5574
- nonce: props.nonce,
5575
- referrerPolicy: props.referrerPolicy,
5576
- };
5577
-}
5578
-
5532
function stylesheetPropsFromPreinitOptions(
5533
href: string,
5534
precedence: string,
5647
}
5648
}
5649
5697
-function markAsImplicitResourceDEV(
5698
- resource: Resource,
5699
- underlyingProps: any,
5700
- impliedProps: any,
5701
-): void {
5702
- if (__DEV__) {
5703
- const devResource: ImplicitResourceDEV = (resource: any);
5704
- if (typeof devResource.__provenance === 'string') {
5705
- console.error(
5706
- 'Resource already marked for DEV type. This is a bug in React.',
5707
- );
5708
- }
5709
- devResource.__provenance = 'implicit';
5710
- devResource.__underlyingProps = underlyingProps;
5711
- devResource.__impliedProps = impliedProps;
5712
- } else {
5713
- // eslint-disable-next-line react-internal/prod-error-codes
5714
- throw new Error(
5715
- 'markAsImplicitResourceDEV was included in a production build. This is a bug in React.',
5716
- );
5717
- }
5718
-}
5719
-
5650
function getAsResourceDEV(
5651
resource: null | void | Resource,
5652
): null | ResourceDEV {