1- import { createResource , type JSX , splitProps } from "solid-js" ;
1+ import { createResource , type JSX , mergeProps , splitProps , untrack } from "solid-js" ;
22import type { DOMElement } from "solid-js/jsx-runtime" ;
33
4- type ScriptAttributes = Omit < JSX . ScriptHTMLAttributes < HTMLScriptElement > , "src" > ;
4+ import type { FetchPriority , ReferrerPolicy , ScriptType } from "./types" ;
5+
6+ type ScriptAttributes = Omit <
7+ JSX . ScriptHTMLAttributes < HTMLScriptElement > ,
8+ "src" | "referrerPolicy" | "fetchpriority" | "type"
9+ > & {
10+ referrerPolicy ?: ReferrerPolicy ;
11+ fetchPriority ?: FetchPriority ;
12+ type ?: ScriptType ;
13+ } ;
514
615type ScriptEvent = Event & {
716 currentTarget : HTMLScriptElement ;
817 target : DOMElement ;
918} ;
1019
20+ type DirectScriptAttributes = Pick <
21+ ScriptAttributes ,
22+ | "async"
23+ | "defer"
24+ | "innerHTML"
25+ | "fetchPriority"
26+ | "noModule"
27+ | "type"
28+ | "crossOrigin"
29+ | "referrerPolicy"
30+ | "integrity"
31+ | "nonce"
32+ > ;
33+
34+ const DIRECT_SCRIPT_ATTRIBUTES = [
35+ "async" ,
36+ "defer" ,
37+ "innerHTML" ,
38+ "fetchPriority" ,
39+ "noModule" ,
40+ "type" ,
41+ "crossOrigin" ,
42+ "referrerPolicy" ,
43+ "integrity" ,
44+ "nonce" ,
45+ ] as Readonly < ( keyof DirectScriptAttributes ) [ ] > ;
46+
1147// Promise cache for script sources
1248const SCRIPT_PROMISES = new Map < string , Promise < Event > > ( ) ;
1349
1450const loadScript = async ( src : string , attributes : Readonly < ScriptAttributes > ) => {
15- const [ initEvents , otherAttributes ] = splitProps ( attributes , [ "onload" , "onLoad" , "onerror" , "onError" ] ) ;
51+ const _attributes = mergeProps (
52+ {
53+ async : true ,
54+ defer : false ,
55+ innerHTML : "" ,
56+ fetchPriority : "auto" as FetchPriority ,
57+ } ,
58+ attributes ,
59+ ) ;
60+
61+ const [ initEvents , otherAttributes ] = splitProps ( _attributes , [
62+ "onload" ,
63+ "onLoad" ,
64+ "onerror" ,
65+ "onError" ,
66+ ] ) ;
67+ const [ directAttributes , restAttributes ] = splitProps ( otherAttributes , DIRECT_SCRIPT_ATTRIBUTES ) ;
1668
1769 // 1. Reject if no src provided
1870 if ( ! src ) return Promise . reject ( new Error ( 'No "src" provided for createScript' ) ) ;
@@ -21,12 +73,36 @@ const loadScript = async (src: string, attributes: Readonly<ScriptAttributes>) =
2173 if ( SCRIPT_PROMISES . has ( src ) ) return SCRIPT_PROMISES . get ( src ) ! ;
2274
2375 // 3. Check if script already exists (may have been added externally not via this hook)
24- if ( document . querySelector ( `script[src="${ src } "]` ) ) return Promise . resolve ( new Event ( "already-loaded" ) ) ;
76+ if ( document . querySelector ( `script[src="${ src } "]` ) )
77+ return Promise . resolve ( new Event ( "already-loaded" ) ) ;
2578
2679 // 4. Create new script element
2780 const promise = new Promise < Event > ( ( resolve , reject ) => {
2881 const script = document . createElement ( "script" ) ;
82+
2983 script . src = src ;
84+ script . async = untrack ( ( ) => directAttributes . async ) ;
85+ script . defer = untrack ( ( ) => directAttributes . defer ) ;
86+ script . innerHTML = untrack ( ( ) => directAttributes . innerHTML ) ;
87+ script . fetchPriority = untrack ( ( ) => directAttributes . fetchPriority ) ;
88+
89+ const type = untrack ( ( ) => directAttributes . type ) ;
90+ if ( type ) script . type = type ;
91+
92+ const crossOrigin = untrack ( ( ) => directAttributes . crossOrigin ) ;
93+ if ( crossOrigin ) script . crossOrigin = crossOrigin ;
94+
95+ const referrerPolicy = untrack ( ( ) => directAttributes . referrerPolicy ) ;
96+ if ( referrerPolicy ) script . referrerPolicy = referrerPolicy ;
97+
98+ const integrity = untrack ( ( ) => directAttributes . integrity ) ;
99+ if ( integrity ) script . integrity = integrity ;
100+
101+ const nonce = untrack ( ( ) => directAttributes . nonce ) ;
102+ if ( nonce ) script . nonce = nonce ;
103+
104+ const noModule = untrack ( ( ) => directAttributes . noModule ) ;
105+ if ( noModule ) script . noModule = noModule ;
30106
31107 script . onload = ( e ) => {
32108 if ( typeof initEvents . onload === "function" ) {
@@ -39,17 +115,23 @@ const loadScript = async (src: string, attributes: Readonly<ScriptAttributes>) =
39115 } ;
40116
41117 script . onerror = ( e ) => {
118+ const event = typeof e === "string" ? new Error ( e ) : e ;
119+ const errorEvent = event as ErrorEvent & {
120+ currentTarget : HTMLScriptElement ;
121+ target : Element ;
122+ } ;
123+
42124 if ( typeof initEvents . onerror === "function" ) {
43- initEvents . onerror ( e as ScriptEvent ) ;
125+ initEvents . onerror ( errorEvent ) ;
44126 } else if ( typeof initEvents . onError === "function" ) {
45- initEvents . onError ( e as ScriptEvent ) ;
127+ initEvents . onError ( errorEvent ) ;
46128 }
47129
48130 reject ( e ) ;
49131 } ;
50132
51133 // Apply additional attributes if any
52- Object . entries ( otherAttributes ) . forEach ( ( [ key , value ] ) => {
134+ Object . entries ( restAttributes ) . forEach ( ( [ key , value ] ) => {
53135 script . setAttribute ( key , value ) ;
54136 } ) ;
55137
0 commit comments