mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
* float enhance!!! Support preinit as script Support resources from async scripts Support saving the precedence place when rendering the shell There was a significant change to the flushing order of resources which follows the general principal of... 1. stuff that blocks display 2. stuff that we know will be used 3. stuff that was explicitly preloaded As a consequence if you preinit a style now it won't automatically flush in the shell unless you actually depend on it in your tree. To avoid races with precedence order we now emit a tag that saves the place amongst the precedence hierarchy so late insertions still end up where they were intended There is also a novel hydration pathway for certain tags. If you render an async script with an onLoad or onError it will always treat it like an insertion rather than a hydration. * restore preinit style flushing behavior and nits
768 lines
21 KiB
JavaScript
768 lines
21 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import {
|
|
validatePreloadResourceDifference,
|
|
validateStyleResourceDifference,
|
|
validateStyleAndHintProps,
|
|
validateScriptResourceDifference,
|
|
validateScriptAndHintProps,
|
|
validateLinkPropsForStyleResource,
|
|
validateLinkPropsForPreloadResource,
|
|
validatePreloadArguments,
|
|
validatePreinitArguments,
|
|
} from '../shared/ReactDOMResourceValidation';
|
|
|
|
type Props = {[string]: mixed};
|
|
|
|
type ResourceType = 'style' | 'font' | 'script';
|
|
|
|
type PreloadProps = {
|
|
rel: 'preload',
|
|
as: ResourceType,
|
|
href: string,
|
|
[string]: mixed,
|
|
};
|
|
type PreloadResource = {
|
|
type: 'preload',
|
|
as: ResourceType,
|
|
href: string,
|
|
props: PreloadProps,
|
|
flushed: boolean,
|
|
};
|
|
|
|
type StyleProps = {
|
|
rel: 'stylesheet',
|
|
href: string,
|
|
'data-precedence': string,
|
|
[string]: mixed,
|
|
};
|
|
type StyleResource = {
|
|
type: 'style',
|
|
href: string,
|
|
precedence: string,
|
|
props: StyleProps,
|
|
|
|
flushed: boolean,
|
|
inShell: boolean, // flushedInShell
|
|
hint: PreloadResource,
|
|
set: Set<StyleResource>, // the precedence set this resource should be flushed in
|
|
};
|
|
|
|
type ScriptProps = {
|
|
src: string,
|
|
[string]: mixed,
|
|
};
|
|
type ScriptResource = {
|
|
type: 'script',
|
|
src: string,
|
|
props: ScriptProps,
|
|
|
|
flushed: boolean,
|
|
hint: PreloadResource,
|
|
};
|
|
|
|
export type Resource = PreloadResource | StyleResource | ScriptResource;
|
|
|
|
export type Resources = {
|
|
// Request local cache
|
|
preloadsMap: Map<string, PreloadResource>,
|
|
stylesMap: Map<string, StyleResource>,
|
|
scriptsMap: Map<string, ScriptResource>,
|
|
|
|
// Flushing queues for Resource dependencies
|
|
fontPreloads: Set<PreloadResource>,
|
|
// usedImagePreloads: Set<PreloadResource>,
|
|
precedences: Map<string, Set<StyleResource>>,
|
|
usedStylePreloads: Set<PreloadResource>,
|
|
scripts: Set<ScriptResource>,
|
|
usedScriptPreloads: Set<PreloadResource>,
|
|
explicitStylePreloads: Set<PreloadResource>,
|
|
// explicitImagePreloads: Set<PreloadResource>,
|
|
explicitScriptPreloads: Set<PreloadResource>,
|
|
|
|
// Module-global-like reference for current boundary resources
|
|
boundaryResources: ?BoundaryResources,
|
|
...
|
|
};
|
|
|
|
// @TODO add bootstrap script to implicit preloads
|
|
export function createResources(): Resources {
|
|
return {
|
|
// persistent
|
|
preloadsMap: new Map(),
|
|
stylesMap: new Map(),
|
|
scriptsMap: new Map(),
|
|
|
|
// cleared on flush
|
|
fontPreloads: new Set(),
|
|
// usedImagePreloads: new Set(),
|
|
precedences: new Map(),
|
|
usedStylePreloads: new Set(),
|
|
scripts: new Set(),
|
|
usedScriptPreloads: new Set(),
|
|
explicitStylePreloads: new Set(),
|
|
// explicitImagePreloads: new Set(),
|
|
explicitScriptPreloads: new Set(),
|
|
|
|
// like a module global for currently rendering boundary
|
|
boundaryResources: null,
|
|
};
|
|
}
|
|
|
|
export type BoundaryResources = Set<StyleResource>;
|
|
|
|
export function createBoundaryResources(): BoundaryResources {
|
|
return new Set();
|
|
}
|
|
|
|
let currentResources: null | Resources = null;
|
|
const currentResourcesStack = [];
|
|
|
|
export function prepareToRenderResources(resources: Resources) {
|
|
currentResourcesStack.push(currentResources);
|
|
currentResources = resources;
|
|
}
|
|
|
|
export function finishRenderingResources() {
|
|
currentResources = currentResourcesStack.pop();
|
|
}
|
|
|
|
export function setCurrentlyRenderingBoundaryResourcesTarget(
|
|
resources: Resources,
|
|
boundaryResources: null | BoundaryResources,
|
|
) {
|
|
resources.boundaryResources = boundaryResources;
|
|
}
|
|
|
|
export const ReactDOMServerDispatcher = {
|
|
preload,
|
|
preinit,
|
|
};
|
|
|
|
type PreloadAs = ResourceType;
|
|
type PreloadOptions = {as: PreloadAs, crossOrigin?: string, integrity?: string};
|
|
function preload(href: string, options: PreloadOptions) {
|
|
if (!currentResources) {
|
|
// While we expect that preload calls are primarily going to be observed
|
|
// during render because effects and events don't run on the server it is
|
|
// still possible that these get called in module scope. This is valid on
|
|
// the client since there is still a document to interact with but on the
|
|
// server we need a request to associate the call to. Because of this we
|
|
// simply return and do not warn.
|
|
return;
|
|
}
|
|
const resources = currentResources;
|
|
if (__DEV__) {
|
|
validatePreloadArguments(href, options);
|
|
}
|
|
if (
|
|
typeof href === 'string' &&
|
|
href &&
|
|
typeof options === 'object' &&
|
|
options !== null
|
|
) {
|
|
const as = options.as;
|
|
let resource = resources.preloadsMap.get(href);
|
|
if (resource) {
|
|
if (__DEV__) {
|
|
const originallyImplicit =
|
|
(resource: any)._dev_implicit_construction === true;
|
|
const latestProps = preloadPropsFromPreloadOptions(href, as, options);
|
|
validatePreloadResourceDifference(
|
|
resource.props,
|
|
originallyImplicit,
|
|
latestProps,
|
|
false,
|
|
);
|
|
}
|
|
} else {
|
|
resource = createPreloadResource(
|
|
resources,
|
|
href,
|
|
as,
|
|
preloadPropsFromPreloadOptions(href, as, options),
|
|
);
|
|
}
|
|
switch (as) {
|
|
case 'font': {
|
|
resources.fontPreloads.add(resource);
|
|
break;
|
|
}
|
|
case 'style': {
|
|
resources.explicitStylePreloads.add(resource);
|
|
break;
|
|
}
|
|
case 'script': {
|
|
resources.explicitScriptPreloads.add(resource);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type PreinitAs = 'style' | 'script';
|
|
type PreinitOptions = {
|
|
as: PreinitAs,
|
|
precedence?: string,
|
|
crossOrigin?: string,
|
|
integrity?: string,
|
|
};
|
|
function preinit(href: string, options: PreinitOptions) {
|
|
if (!currentResources) {
|
|
// While we expect that preinit calls are primarily going to be observed
|
|
// during render because effects and events don't run on the server it is
|
|
// still possible that these get called in module scope. This is valid on
|
|
// the client since there is still a document to interact with but on the
|
|
// server we need a request to associate the call to. Because of this we
|
|
// simply return and do not warn.
|
|
return;
|
|
}
|
|
const resources = currentResources;
|
|
if (__DEV__) {
|
|
validatePreinitArguments(href, options);
|
|
}
|
|
if (
|
|
typeof href === 'string' &&
|
|
href &&
|
|
typeof options === 'object' &&
|
|
options !== null
|
|
) {
|
|
const as = options.as;
|
|
switch (as) {
|
|
case 'style': {
|
|
let resource = resources.stylesMap.get(href);
|
|
if (resource) {
|
|
if (__DEV__) {
|
|
const latestProps = stylePropsFromPreinitOptions(
|
|
href,
|
|
resource.precedence,
|
|
options,
|
|
);
|
|
validateStyleResourceDifference(resource.props, latestProps);
|
|
}
|
|
} else {
|
|
const precedence = options.precedence || 'default';
|
|
const resourceProps = stylePropsFromPreinitOptions(
|
|
href,
|
|
precedence,
|
|
options,
|
|
);
|
|
resource = createStyleResource(
|
|
resources,
|
|
href,
|
|
precedence,
|
|
resourceProps,
|
|
);
|
|
}
|
|
resource.set.add(resource);
|
|
resources.explicitStylePreloads.add(resource.hint);
|
|
|
|
return;
|
|
}
|
|
case 'script': {
|
|
const src = href;
|
|
let resource = resources.scriptsMap.get(src);
|
|
if (resource) {
|
|
if (__DEV__) {
|
|
const latestProps = scriptPropsFromPreinitOptions(src, options);
|
|
validateScriptResourceDifference(resource.props, latestProps);
|
|
}
|
|
} else {
|
|
const scriptProps = scriptPropsFromPreinitOptions(src, options);
|
|
resource = createScriptResource(resources, src, scriptProps);
|
|
resources.scripts.add(resource);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function preloadPropsFromPreloadOptions(
|
|
href: string,
|
|
as: ResourceType,
|
|
options: PreloadOptions,
|
|
): PreloadProps {
|
|
return {
|
|
href,
|
|
rel: 'preload',
|
|
as,
|
|
crossOrigin: as === 'font' ? '' : options.crossOrigin,
|
|
integrity: options.integrity,
|
|
};
|
|
}
|
|
|
|
function preloadPropsFromRawProps(
|
|
href: string,
|
|
as: ResourceType,
|
|
rawProps: Props,
|
|
): PreloadProps {
|
|
const props: PreloadProps = Object.assign({}, rawProps);
|
|
props.href = href;
|
|
props.rel = 'preload';
|
|
props.as = as;
|
|
if (as === 'font') {
|
|
// Font preloads always need CORS anonymous mode so we set it here
|
|
// regardless of the props provided. This should warn elsewhere in
|
|
// dev
|
|
props.crossOrigin = '';
|
|
}
|
|
return props;
|
|
}
|
|
|
|
function preloadAsStylePropsFromProps(
|
|
href: string,
|
|
props: Props | StyleProps,
|
|
): PreloadProps {
|
|
return {
|
|
rel: 'preload',
|
|
as: 'style',
|
|
href: href,
|
|
crossOrigin: props.crossOrigin,
|
|
integrity: props.integrity,
|
|
media: props.media,
|
|
hrefLang: props.hrefLang,
|
|
referrerPolicy: props.referrerPolicy,
|
|
};
|
|
}
|
|
|
|
function preloadAsScriptPropsFromProps(
|
|
href: string,
|
|
props: Props | ScriptProps,
|
|
): PreloadProps {
|
|
return {
|
|
rel: 'preload',
|
|
as: 'script',
|
|
href,
|
|
crossOrigin: props.crossOrigin,
|
|
integrity: props.integrity,
|
|
referrerPolicy: props.referrerPolicy,
|
|
};
|
|
}
|
|
|
|
function createPreloadResource(
|
|
resources: Resources,
|
|
href: string,
|
|
as: ResourceType,
|
|
props: PreloadProps,
|
|
): PreloadResource {
|
|
const {preloadsMap} = resources;
|
|
if (__DEV__) {
|
|
if (preloadsMap.has(href)) {
|
|
console.error(
|
|
'createPreloadResource was called when a preload Resource matching the same href already exists. This is a bug in React.',
|
|
);
|
|
}
|
|
}
|
|
|
|
const resource = {
|
|
type: 'preload',
|
|
as,
|
|
href,
|
|
flushed: false,
|
|
props,
|
|
};
|
|
preloadsMap.set(href, resource);
|
|
return resource;
|
|
}
|
|
|
|
function stylePropsFromRawProps(
|
|
href: string,
|
|
precedence: string,
|
|
rawProps: Props,
|
|
): StyleProps {
|
|
const props: StyleProps = Object.assign({}, rawProps);
|
|
props.href = href;
|
|
props.rel = 'stylesheet';
|
|
props['data-precedence'] = precedence;
|
|
delete props.precedence;
|
|
|
|
return props;
|
|
}
|
|
|
|
function stylePropsFromPreinitOptions(
|
|
href: string,
|
|
precedence: string,
|
|
options: PreinitOptions,
|
|
): StyleProps {
|
|
return {
|
|
rel: 'stylesheet',
|
|
href,
|
|
'data-precedence': precedence,
|
|
crossOrigin: options.crossOrigin,
|
|
};
|
|
}
|
|
|
|
function createStyleResource(
|
|
resources: Resources,
|
|
href: string,
|
|
precedence: string,
|
|
props: StyleProps,
|
|
): StyleResource {
|
|
if (__DEV__) {
|
|
if (resources.stylesMap.has(href)) {
|
|
console.error(
|
|
'createStyleResource was called when a style Resource matching the same href already exists. This is a bug in React.',
|
|
);
|
|
}
|
|
}
|
|
const {stylesMap, preloadsMap, precedences} = resources;
|
|
|
|
// If this is the first time we've seen this precedence we encode it's position in our set even though
|
|
// we don't add the resource to this set yet
|
|
let precedenceSet = precedences.get(precedence);
|
|
if (!precedenceSet) {
|
|
precedenceSet = new Set();
|
|
precedences.set(precedence, precedenceSet);
|
|
}
|
|
|
|
let hint = preloadsMap.get(href);
|
|
if (hint) {
|
|
// If a preload for this style Resource already exists there are certain props we want to adopt
|
|
// on the style Resource, primarily focussed on making sure the style network pathways utilize
|
|
// the preload pathways. For instance if you have diffreent crossOrigin attributes for a preload
|
|
// and a stylesheet the stylesheet will make a new request even if the preload had already loaded
|
|
adoptPreloadPropsForStyleProps(props, hint.props);
|
|
|
|
if (__DEV__) {
|
|
validateStyleAndHintProps(
|
|
hint.props,
|
|
props,
|
|
(hint: any)._dev_implicit_construction,
|
|
);
|
|
}
|
|
} else {
|
|
const preloadResourceProps = preloadAsStylePropsFromProps(href, props);
|
|
hint = createPreloadResource(
|
|
resources,
|
|
href,
|
|
'style',
|
|
preloadResourceProps,
|
|
);
|
|
if (__DEV__) {
|
|
(hint: any)._dev_implicit_construction = true;
|
|
}
|
|
resources.explicitStylePreloads.add(hint);
|
|
}
|
|
|
|
const resource = {
|
|
type: 'style',
|
|
href,
|
|
precedence,
|
|
flushed: false,
|
|
inShell: false,
|
|
props,
|
|
hint,
|
|
set: precedenceSet,
|
|
};
|
|
stylesMap.set(href, resource);
|
|
|
|
return resource;
|
|
}
|
|
|
|
function adoptPreloadPropsForStyleProps(
|
|
resourceProps: StyleProps,
|
|
preloadProps: PreloadProps,
|
|
): void {
|
|
if (resourceProps.crossOrigin == null)
|
|
resourceProps.crossOrigin = preloadProps.crossOrigin;
|
|
if (resourceProps.referrerPolicy == null)
|
|
resourceProps.referrerPolicy = preloadProps.referrerPolicy;
|
|
if (resourceProps.title == null) resourceProps.title = preloadProps.title;
|
|
}
|
|
|
|
function scriptPropsFromPreinitOptions(
|
|
src: string,
|
|
options: PreinitOptions,
|
|
): ScriptProps {
|
|
return {
|
|
src,
|
|
async: true,
|
|
crossOrigin: options.crossOrigin,
|
|
integrity: options.integrity,
|
|
};
|
|
}
|
|
|
|
function scriptPropsFromRawProps(src: string, rawProps: Props): ScriptProps {
|
|
const props = Object.assign({}, rawProps);
|
|
props.src = src;
|
|
return props;
|
|
}
|
|
|
|
function createScriptResource(
|
|
resources: Resources,
|
|
src: string,
|
|
props: ScriptProps,
|
|
): ScriptResource {
|
|
if (__DEV__) {
|
|
if (resources.scriptsMap.has(src)) {
|
|
console.error(
|
|
'createScriptResource was called when a script Resource matching the same src already exists. This is a bug in React.',
|
|
);
|
|
}
|
|
}
|
|
const {scriptsMap, preloadsMap} = resources;
|
|
|
|
let hint = preloadsMap.get(src);
|
|
if (hint) {
|
|
// If a preload for this style Resource already exists there are certain props we want to adopt
|
|
// on the style Resource, primarily focussed on making sure the style network pathways utilize
|
|
// the preload pathways. For instance if you have diffreent crossOrigin attributes for a preload
|
|
// and a stylesheet the stylesheet will make a new request even if the preload had already loaded
|
|
adoptPreloadPropsForScriptProps(props, hint.props);
|
|
|
|
if (__DEV__) {
|
|
validateScriptAndHintProps(
|
|
hint.props,
|
|
props,
|
|
(hint: any)._dev_implicit_construction,
|
|
);
|
|
}
|
|
} else {
|
|
const preloadResourceProps = preloadAsScriptPropsFromProps(src, props);
|
|
hint = createPreloadResource(
|
|
resources,
|
|
src,
|
|
'script',
|
|
preloadResourceProps,
|
|
);
|
|
if (__DEV__) {
|
|
(hint: any)._dev_implicit_construction = true;
|
|
}
|
|
resources.explicitScriptPreloads.add(hint);
|
|
}
|
|
|
|
const resource = {
|
|
type: 'script',
|
|
src,
|
|
flushed: false,
|
|
props,
|
|
hint,
|
|
};
|
|
scriptsMap.set(src, resource);
|
|
|
|
return resource;
|
|
}
|
|
|
|
function adoptPreloadPropsForScriptProps(
|
|
resourceProps: ScriptProps,
|
|
preloadProps: PreloadProps,
|
|
): void {
|
|
if (resourceProps.crossOrigin == null)
|
|
resourceProps.crossOrigin = preloadProps.crossOrigin;
|
|
if (resourceProps.referrerPolicy == null)
|
|
resourceProps.referrerPolicy = preloadProps.referrerPolicy;
|
|
if (resourceProps.integrity == null)
|
|
resourceProps.integrity = preloadProps.integrity;
|
|
}
|
|
|
|
// Construct a resource from link props.
|
|
export function resourcesFromLink(props: Props): boolean {
|
|
if (!currentResources) {
|
|
throw new Error(
|
|
'"currentResources" was expected to exist. This is a bug in React.',
|
|
);
|
|
}
|
|
const resources = currentResources;
|
|
|
|
const {rel, href} = props;
|
|
if (!href || typeof href !== 'string') {
|
|
return false;
|
|
}
|
|
|
|
switch (rel) {
|
|
case 'stylesheet': {
|
|
const {onLoad, onError, precedence, disabled} = props;
|
|
if (
|
|
typeof precedence !== 'string' ||
|
|
onLoad ||
|
|
onError ||
|
|
disabled != null
|
|
) {
|
|
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
|
|
// disqualify it for such. We can still create a preload resource to help it load faster on the
|
|
// client
|
|
if (__DEV__) {
|
|
validateLinkPropsForStyleResource(props);
|
|
}
|
|
// $FlowFixMe[incompatible-use] found when upgrading Flow
|
|
let preloadResource = resources.preloadsMap.get(href);
|
|
if (!preloadResource) {
|
|
preloadResource = createPreloadResource(
|
|
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
|
resources,
|
|
href,
|
|
'style',
|
|
preloadAsStylePropsFromProps(href, props),
|
|
);
|
|
if (__DEV__) {
|
|
(preloadResource: any)._dev_implicit_construction = true;
|
|
}
|
|
resources.usedStylePreloads.add(preloadResource);
|
|
}
|
|
return false;
|
|
} else {
|
|
// We are able to convert this link element to a resource exclusively. We construct the relevant Resource
|
|
// and return true indicating that this link was fully consumed.
|
|
let resource = resources.stylesMap.get(href);
|
|
|
|
if (resource) {
|
|
if (__DEV__) {
|
|
const resourceProps = stylePropsFromRawProps(
|
|
href,
|
|
precedence,
|
|
props,
|
|
);
|
|
adoptPreloadPropsForStyleProps(resourceProps, resource.hint.props);
|
|
validateStyleResourceDifference(resource.props, resourceProps);
|
|
}
|
|
} else {
|
|
const resourceProps = stylePropsFromRawProps(href, precedence, props);
|
|
resource = createStyleResource(
|
|
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
|
currentResources,
|
|
href,
|
|
precedence,
|
|
resourceProps,
|
|
);
|
|
resources.usedStylePreloads.add(resource.hint);
|
|
}
|
|
if (resources.boundaryResources) {
|
|
resources.boundaryResources.add(resource);
|
|
} else {
|
|
resource.set.add(resource);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
case 'preload': {
|
|
const {as} = props;
|
|
switch (as) {
|
|
case 'script':
|
|
case 'style':
|
|
case 'font': {
|
|
if (__DEV__) {
|
|
validateLinkPropsForPreloadResource(props);
|
|
}
|
|
let resource = resources.preloadsMap.get(href);
|
|
if (resource) {
|
|
if (__DEV__) {
|
|
const originallyImplicit =
|
|
(resource: any)._dev_implicit_construction === true;
|
|
const latestProps = preloadPropsFromRawProps(href, as, props);
|
|
validatePreloadResourceDifference(
|
|
resource.props,
|
|
originallyImplicit,
|
|
latestProps,
|
|
false,
|
|
);
|
|
}
|
|
} else {
|
|
resource = createPreloadResource(
|
|
resources,
|
|
href,
|
|
as,
|
|
preloadPropsFromRawProps(href, as, props),
|
|
);
|
|
switch (as) {
|
|
case 'script': {
|
|
resources.explicitScriptPreloads.add(resource);
|
|
break;
|
|
}
|
|
case 'style': {
|
|
resources.explicitStylePreloads.add(resource);
|
|
break;
|
|
}
|
|
case 'font': {
|
|
resources.fontPreloads.add(resource);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Construct a resource from link props.
|
|
export function resourcesFromScript(props: Props): boolean {
|
|
if (!currentResources) {
|
|
throw new Error(
|
|
'"currentResources" was expected to exist. This is a bug in React.',
|
|
);
|
|
}
|
|
const resources = currentResources;
|
|
const {src, async, onLoad, onError} = props;
|
|
if (!src || typeof src !== 'string') {
|
|
return false;
|
|
}
|
|
|
|
if (async) {
|
|
if (onLoad || onError) {
|
|
if (__DEV__) {
|
|
// validate
|
|
}
|
|
let preloadResource = resources.preloadsMap.get(src);
|
|
if (!preloadResource) {
|
|
preloadResource = createPreloadResource(
|
|
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
|
resources,
|
|
src,
|
|
'script',
|
|
preloadAsScriptPropsFromProps(src, props),
|
|
);
|
|
if (__DEV__) {
|
|
(preloadResource: any)._dev_implicit_construction = true;
|
|
}
|
|
resources.usedScriptPreloads.add(preloadResource);
|
|
}
|
|
} else {
|
|
let resource = resources.scriptsMap.get(src);
|
|
if (resource) {
|
|
if (__DEV__) {
|
|
const latestProps = scriptPropsFromRawProps(src, props);
|
|
adoptPreloadPropsForScriptProps(latestProps, resource.hint.props);
|
|
validateScriptResourceDifference(resource.props, latestProps);
|
|
}
|
|
} else {
|
|
const resourceProps = scriptPropsFromRawProps(src, props);
|
|
resource = createScriptResource(resources, src, resourceProps);
|
|
resources.scripts.add(resource);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
export function hoistResources(
|
|
resources: Resources,
|
|
source: BoundaryResources,
|
|
): void {
|
|
const currentBoundaryResources = resources.boundaryResources;
|
|
if (currentBoundaryResources) {
|
|
source.forEach(resource => currentBoundaryResources.add(resource));
|
|
source.clear();
|
|
}
|
|
}
|
|
|
|
export function hoistResourcesToRoot(
|
|
resources: Resources,
|
|
boundaryResources: BoundaryResources,
|
|
): void {
|
|
boundaryResources.forEach(resource => resource.set.add(resource));
|
|
boundaryResources.clear();
|
|
}
|