@@ -2543,18 +2543,10 @@ export function writeCompletedBoundaryInstruction(
2543
2543
if ( scriptFormat ) {
2544
2544
writeChunk ( destination , completeBoundaryScript3a ) ;
2545
2545
// boundaryResources encodes an array literal
2546
- writeStyleResourceDependencies (
2547
- destination ,
2548
- boundaryResources ,
2549
- scriptFormat ? JavascriptEmbedding : HTMLAttributeEmbedding ,
2550
- ) ;
2546
+ writeStyleResourceDependenciesInJS ( destination , boundaryResources ) ;
2551
2547
} else {
2552
2548
writeChunk ( destination , completeBoundaryData3aStart ) ;
2553
- writeStyleResourceDependencies (
2554
- destination ,
2555
- boundaryResources ,
2556
- scriptFormat ? JavascriptEmbedding : HTMLAttributeEmbedding ,
2557
- ) ;
2549
+ writeStyleResourceDependenciesInAttr ( destination , boundaryResources ) ;
2558
2550
writeChunk ( destination , completeBoundaryData3aEnd ) ;
2559
2551
}
2560
2552
} else {
@@ -2996,40 +2988,201 @@ const arraySubsequentOpenBracket = stringToPrecomputedChunk(',[');
2996
2988
const arrayInterstitial = stringToPrecomputedChunk ( ',' ) ;
2997
2989
const arrayCloseBracket = stringToPrecomputedChunk ( ']' ) ;
2998
2990
2999
- type EmbeddingContext = 0 | 1 ;
3000
- const HTMLAttributeEmbedding : EmbeddingContext = 0 ;
3001
- const JavascriptEmbedding : EmbeddingContext = 1 ;
2991
+ // This function writes a 2D array of strings to be embedded in javascript.
2992
+ // E.g.
2993
+ // [["JS_escaped_string1", "JS_escaped_string2"]]
2994
+ function writeStyleResourceDependenciesInJS (
2995
+ destination : Destination ,
2996
+ boundaryResources : BoundaryResources ,
2997
+ ) : void {
2998
+ writeChunk ( destination , arrayFirstOpenBracket ) ;
3002
2999
3003
- function writeStyleResourceObject (
3000
+ let nextArrayOpenBrackChunk = arrayFirstOpenBracket ;
3001
+ boundaryResources . forEach ( resource => {
3002
+ if ( resource . inShell ) {
3003
+ // We can elide this dependency because it was flushed in the shell and
3004
+ // should be ready before content is shown on the client
3005
+ } else if ( resource . flushed ) {
3006
+ writeChunk ( destination , nextArrayOpenBrackChunk ) ;
3007
+ writeStyleResourceDependencyHrefOnlyInJS ( destination , resource . href ) ;
3008
+ writeChunk ( destination , arrayCloseBracket ) ;
3009
+ nextArrayOpenBrackChunk = arraySubsequentOpenBracket ;
3010
+ } else {
3011
+ writeChunk ( destination , nextArrayOpenBrackChunk ) ;
3012
+ writeStyleResourceDependencyInJS (
3013
+ destination ,
3014
+ resource . href ,
3015
+ resource . precedence ,
3016
+ resource . props ,
3017
+ ) ;
3018
+ writeChunk ( destination , arrayCloseBracket ) ;
3019
+ nextArrayOpenBrackChunk = arraySubsequentOpenBracket ;
3020
+
3021
+ resource . flushed = true ;
3022
+ resource . hint . flushed = true ;
3023
+ }
3024
+ } ) ;
3025
+ writeChunk ( destination , arrayCloseBracket ) ;
3026
+ }
3027
+
3028
+ /* Helper functions */
3029
+ function writeStyleResourceDependencyHrefOnlyInJS (
3004
3030
destination : Destination ,
3005
- str : string ,
3006
- context : EmbeddingContext ,
3031
+ href : string ,
3007
3032
) {
3008
- if ( context === JavascriptEmbedding ) {
3009
- // write "script_escaped_string", since this is writing to a script tag
3010
- // and will be evaluated as a string literal inside an array literal
3011
- writeChunk (
3012
- destination ,
3013
- stringToChunk ( escapeJSObjectForInstructionScripts ( str ) ) ,
3014
- ) ;
3015
- } else if ( context === HTMLAttributeEmbedding ) {
3016
- // write "JSON_escaped_string" here, since this is writing
3017
- // to an attribute string and will be parsed manually via JSON.parse
3018
- writeChunk (
3019
- destination ,
3020
- stringToChunk ( escapeTextForBrowser ( JSON . stringify ( str ) ) ) ,
3021
- ) ;
3022
- } else {
3023
- throw new Error (
3024
- 'Unknown embedding context type when writing style resources. This is a bug in React.' ,
3025
- ) ;
3033
+ // We should actually enforce this earlier when the resource is created but for
3034
+ // now we make sure we are actually dealing with a string here.
3035
+ if ( __DEV__ ) {
3036
+ checkAttributeStringCoercion ( href , 'href' ) ;
3026
3037
}
3038
+ const coercedHref = '' + ( href : any ) ;
3039
+ writeChunk (
3040
+ destination ,
3041
+ stringToChunk ( escapeJSObjectForInstructionScripts ( coercedHref ) ) ,
3042
+ ) ;
3027
3043
}
3028
3044
3029
- function writeStyleResourceDependencies (
3045
+ function writeStyleResourceDependencyInJS (
3046
+ destination : Destination ,
3047
+ href : string ,
3048
+ precedence : string ,
3049
+ props : Object ,
3050
+ ) {
3051
+ if ( __DEV__ ) {
3052
+ checkAttributeStringCoercion ( href , 'href' ) ;
3053
+ }
3054
+ const coercedHref = '' + ( href : any ) ;
3055
+ sanitizeURL ( coercedHref ) ;
3056
+ writeChunk (
3057
+ destination ,
3058
+ stringToChunk ( escapeJSObjectForInstructionScripts ( coercedHref ) ) ,
3059
+ ) ;
3060
+
3061
+ if ( __DEV__ ) {
3062
+ checkAttributeStringCoercion ( precedence , 'precedence' ) ;
3063
+ }
3064
+ const coercedPrecedence = '' + ( precedence : any ) ;
3065
+ writeChunk ( destination , arrayInterstitial ) ;
3066
+ writeChunk (
3067
+ destination ,
3068
+ stringToChunk ( escapeJSObjectForInstructionScripts ( coercedPrecedence ) ) ,
3069
+ ) ;
3070
+
3071
+ for ( const propKey in props ) {
3072
+ if ( hasOwnProperty . call ( props , propKey ) ) {
3073
+ const propValue = props [ propKey ] ;
3074
+ if ( propValue == null ) {
3075
+ continue ;
3076
+ }
3077
+ switch ( propKey ) {
3078
+ case 'href ':
3079
+ case 'rel ':
3080
+ case 'precedence ':
3081
+ case 'data - precedence ': {
3082
+ break ;
3083
+ }
3084
+ case ' children ':
3085
+ case ' dangerouslySetInnerHTML ':
3086
+ throw new Error (
3087
+ `${ 'link' } is a self-closing tag and must neither have \`children\` nor ` +
3088
+ 'use `dangerouslySetInnerHTML`.' ,
3089
+ ) ;
3090
+ // eslint-disable-next-line-no-fallthrough
3091
+ default :
3092
+ writeStyleResourceAttributeInJS ( destination , propKey , propValue ) ;
3093
+ break ;
3094
+ }
3095
+ }
3096
+ }
3097
+ return null ;
3098
+ }
3099
+
3100
+ function writeStyleResourceAttributeInJS (
3101
+ destination : Destination ,
3102
+ name : string ,
3103
+ value : string | boolean | number | Function | Object , // not null or undefined
3104
+ ) : void {
3105
+ let attributeName = name . toLowerCase ( ) ;
3106
+ let attributeValue ;
3107
+ switch ( typeof value ) {
3108
+ case 'function ':
3109
+ case 'symbol ':
3110
+ return ;
3111
+ }
3112
+
3113
+ switch ( name ) {
3114
+ // Reserved names
3115
+ case 'innerHTML ':
3116
+ case 'dangerouslySetInnerHTML ':
3117
+ case 'suppressContentEditableWarning ':
3118
+ case 'suppressHydrationWarning ':
3119
+ case 'style ':
3120
+ // Ignored
3121
+ return ;
3122
+
3123
+ // Attribute renames
3124
+ case 'className ':
3125
+ attributeName = 'class ';
3126
+ break ;
3127
+
3128
+ // Booleans
3129
+ case 'hidden ':
3130
+ if ( value === false ) {
3131
+ return ;
3132
+ }
3133
+ attributeValue = '';
3134
+ break ;
3135
+
3136
+ // Santized URLs
3137
+ case 'src ':
3138
+ case 'href ': {
3139
+ if ( __DEV__ ) {
3140
+ checkAttributeStringCoercion ( value , attributeName ) ;
3141
+ }
3142
+ attributeValue = '' + ( value : any ) ;
3143
+ sanitizeURL ( attributeValue ) ;
3144
+ break ;
3145
+ }
3146
+ default : {
3147
+ if ( ! isAttributeNameSafe ( name ) ) {
3148
+ return ;
3149
+ }
3150
+ }
3151
+ }
3152
+
3153
+ if (
3154
+ // shouldIgnoreAttribute
3155
+ // We have already filtered out null/undefined and reserved words.
3156
+ name . length > 2 &&
3157
+ ( name [ 0 ] === 'o' || name [ 0 ] === 'O' ) &&
3158
+ ( name [ 1 ] === 'n' || name [ 1 ] === 'N' )
3159
+ ) {
3160
+ return ;
3161
+ }
3162
+
3163
+ if ( __DEV__ ) {
3164
+ checkAttributeStringCoercion ( value , attributeName ) ;
3165
+ }
3166
+ attributeValue = '' + ( value : any ) ;
3167
+ writeChunk ( destination , arrayInterstitial ) ;
3168
+ writeChunk (
3169
+ destination ,
3170
+ stringToChunk ( escapeJSObjectForInstructionScripts ( attributeName ) ) ,
3171
+ ) ;
3172
+ writeChunk ( destination , arrayInterstitial ) ;
3173
+ writeChunk (
3174
+ destination ,
3175
+ stringToChunk ( escapeJSObjectForInstructionScripts ( attributeValue ) ) ,
3176
+ ) ;
3177
+ }
3178
+
3179
+ // This function writes a 2D array of strings to be embedded in an attribute
3180
+ // value and read with JSON.parse in ReactDOMServerExternalRuntime.js
3181
+ // E.g.
3182
+ // [["JSON_escaped_string1", "JSON_escaped_string2"]]
3183
+ function writeStyleResourceDependenciesInAttr (
3030
3184
destination : Destination ,
3031
3185
boundaryResources : BoundaryResources ,
3032
- context : EmbeddingContext ,
3033
3186
) : void {
3034
3187
writeChunk ( destination , arrayFirstOpenBracket ) ;
3035
3188
@@ -3040,17 +3193,16 @@ function writeStyleResourceDependencies(
3040
3193
// should be ready before content is shown on the client
3041
3194
} else if ( resource . flushed ) {
3042
3195
writeChunk ( destination , nextArrayOpenBrackChunk ) ;
3043
- writeStyleResourceDependencyHrefOnly ( destination , resource . href , context ) ;
3196
+ writeStyleResourceDependencyHrefOnlyInAttr ( destination , resource . href ) ;
3044
3197
writeChunk ( destination , arrayCloseBracket ) ;
3045
3198
nextArrayOpenBrackChunk = arraySubsequentOpenBracket ;
3046
3199
} else {
3047
3200
writeChunk ( destination , nextArrayOpenBrackChunk ) ;
3048
- writeStyleResourceDependency (
3201
+ writeStyleResourceDependencyInAttr (
3049
3202
destination ,
3050
3203
resource . href ,
3051
3204
resource . precedence ,
3052
3205
resource . props ,
3053
- context ,
3054
3206
) ;
3055
3207
writeChunk ( destination , arrayCloseBracket ) ;
3056
3208
nextArrayOpenBrackChunk = arraySubsequentOpenBracket ;
@@ -3062,41 +3214,48 @@ function writeStyleResourceDependencies(
3062
3214
writeChunk ( destination , arrayCloseBracket ) ;
3063
3215
}
3064
3216
3065
- function writeStyleResourceDependencyHrefOnly (
3217
+ /* Helper functions */
3218
+ function writeStyleResourceDependencyHrefOnlyInAttr (
3066
3219
destination : Destination ,
3067
3220
href : string ,
3068
- context : EmbeddingContext ,
3069
3221
) {
3070
3222
// We should actually enforce this earlier when the resource is created but for
3071
3223
// now we make sure we are actually dealing with a string here.
3072
3224
if ( __DEV__ ) {
3073
3225
checkAttributeStringCoercion ( href , 'href' ) ;
3074
3226
}
3075
3227
const coercedHref = '' + ( href : any ) ;
3076
- writeStyleResourceObject ( destination , coercedHref , context ) ;
3228
+ writeChunk (
3229
+ destination ,
3230
+ stringToChunk ( escapeTextForBrowser ( JSON . stringify ( coercedHref ) ) ) ,
3231
+ ) ;
3077
3232
}
3078
3233
3079
- function writeStyleResourceDependency (
3234
+ function writeStyleResourceDependencyInAttr (
3080
3235
destination : Destination ,
3081
3236
href : string ,
3082
3237
precedence : string ,
3083
3238
props : Object ,
3084
- context : EmbeddingContext ,
3085
3239
) {
3086
3240
if ( __DEV__ ) {
3087
3241
checkAttributeStringCoercion ( href , 'href' ) ;
3088
3242
}
3089
3243
const coercedHref = '' + ( href : any ) ;
3090
3244
sanitizeURL ( coercedHref ) ;
3091
- writeStyleResourceObject ( destination , coercedHref , context ) ;
3245
+ writeChunk (
3246
+ destination ,
3247
+ stringToChunk ( escapeTextForBrowser ( JSON . stringify ( coercedHref ) ) ) ,
3248
+ ) ;
3092
3249
3093
3250
if ( __DEV__ ) {
3094
3251
checkAttributeStringCoercion ( precedence , 'precedence' ) ;
3095
3252
}
3096
3253
const coercedPrecedence = '' + ( precedence : any ) ;
3097
3254
writeChunk ( destination , arrayInterstitial ) ;
3098
-
3099
- writeStyleResourceObject ( destination , coercedPrecedence , context ) ;
3255
+ writeChunk (
3256
+ destination ,
3257
+ stringToChunk ( escapeTextForBrowser ( JSON . stringify ( coercedPrecedence ) ) ) ,
3258
+ ) ;
3100
3259
3101
3260
for ( const propKey in props ) {
3102
3261
if ( hasOwnProperty . call ( props , propKey ) ) {
@@ -3119,19 +3278,18 @@ function writeStyleResourceDependency(
3119
3278
) ;
3120
3279
// eslint-disable-next-line-no-fallthrough
3121
3280
default :
3122
- writeStyleResourceAttribute ( destination , propKey , propValue , context ) ;
3281
+ writeStyleResourceAttributeInAttr ( destination , propKey , propValue ) ;
3123
3282
break ;
3124
3283
}
3125
3284
}
3126
3285
}
3127
3286
return null ;
3128
3287
}
3129
3288
3130
- function writeStyleResourceAttribute (
3289
+ function writeStyleResourceAttributeInAttr (
3131
3290
destination : Destination ,
3132
3291
name : string ,
3133
3292
value : string | boolean | number | Function | Object , // not null or undefined
3134
- context : EmbeddingContext ,
3135
3293
) : void {
3136
3294
let attributeName = name . toLowerCase ( ) ;
3137
3295
let attributeValue ;
@@ -3196,7 +3354,13 @@ function writeStyleResourceAttribute(
3196
3354
}
3197
3355
attributeValue = '' + ( value : any ) ;
3198
3356
writeChunk ( destination , arrayInterstitial ) ;
3199
- writeStyleResourceObject ( destination , attributeName , context ) ;
3357
+ writeChunk (
3358
+ destination ,
3359
+ stringToChunk ( escapeTextForBrowser ( JSON . stringify ( attributeName ) ) ) ,
3360
+ ) ;
3200
3361
writeChunk ( destination , arrayInterstitial ) ;
3201
- writeStyleResourceObject ( destination , attributeValue , context ) ;
3362
+ writeChunk (
3363
+ destination ,
3364
+ stringToChunk ( escapeTextForBrowser ( JSON . stringify ( attributeValue ) ) ) ,
3365
+ ) ;
3202
3366
}
0 commit comments