@@ -8,7 +8,11 @@ import {
88 stripEmbeddedRuntimeScripts ,
99} from "./htmlDocument" ;
1010// rewriteSubCompPaths functions are used by inlineSubCompositions (shared module)
11- import { scopeCssToComposition , wrapScopedCompositionScript } from "./compositionScoping" ;
11+ import {
12+ scopeCssToComposition ,
13+ wrapInlineScriptWithErrorBoundary ,
14+ wrapScopedCompositionScript ,
15+ } from "./compositionScoping" ;
1216import { validateHyperframeHtmlContract } from "./staticGuard" ;
1317import { getHyperframeRuntimeScript } from "../generated/runtime-inline" ;
1418import { readDeclaredDefaults } from "../runtime/getVariables" ;
@@ -718,12 +722,34 @@ export async function bundleToSingleHtml(
718722 } ,
719723 } ) ;
720724 const compStyleChunks : string [ ] = [ ...subCompResult . styles ] ;
721- const compScriptChunks : string [ ] = [ ...subCompResult . scripts ] ;
722- const compExternalScriptSrcs : string [ ] = [ ...subCompResult . externalScriptSrcs ] ;
725+ const compScriptChunks : string [ ] = [ ] ;
723726 const compExternalLinks = [ ...subCompResult . externalLinks ] ;
724727 const compVariablesByComp : Record < string , Record < string , unknown > > = {
725728 ...subCompResult . variablesByComp ,
726729 } ;
730+ const seenCompScriptSrcs = new Set < string > ( ) ;
731+ for ( const scriptItem of subCompResult . scriptItems ) {
732+ if ( scriptItem . kind === "inline" ) {
733+ compScriptChunks . push ( scriptItem . content ) ;
734+ continue ;
735+ }
736+ const extSrc = scriptItem . src ;
737+ if ( seenCompScriptSrcs . has ( extSrc ) ) continue ;
738+ seenCompScriptSrcs . add ( extSrc ) ;
739+ if ( isRelativeUrl ( extSrc ) ) {
740+ const jsPath = safePath ( projectDir , extSrc ) ;
741+ const js = jsPath ? safeReadFile ( jsPath ) : null ;
742+ if ( js != null ) {
743+ compScriptChunks . push ( js ) ;
744+ continue ;
745+ }
746+ }
747+ if ( ! document . querySelector ( `script[src="${ extSrc } "]` ) ) {
748+ const extScript = document . createElement ( "script" ) ;
749+ extScript . setAttribute ( "src" , extSrc ) ;
750+ document . body . appendChild ( extScript ) ;
751+ }
752+ }
727753
728754 // Inline template compositions: inject <template id="X-template"> content into
729755 // matching empty host elements with data-composition-id="X" (no data-composition-src)
@@ -773,8 +799,23 @@ export async function bundleToSingleHtml(
773799 for ( const scriptEl of [ ...innerRoot . querySelectorAll ( "script" ) ] ) {
774800 const externalSrc = ( scriptEl . getAttribute ( "src" ) || "" ) . trim ( ) ;
775801 if ( externalSrc ) {
776- if ( ! compExternalScriptSrcs . includes ( externalSrc ) ) {
777- compExternalScriptSrcs . push ( externalSrc ) ;
802+ if ( ! seenCompScriptSrcs . has ( externalSrc ) ) {
803+ seenCompScriptSrcs . add ( externalSrc ) ;
804+ if ( isRelativeUrl ( externalSrc ) ) {
805+ const jsPath = safePath ( projectDir , externalSrc ) ;
806+ const js = jsPath ? safeReadFile ( jsPath ) : null ;
807+ if ( js != null ) {
808+ compScriptChunks . push ( js ) ;
809+ } else if ( ! document . querySelector ( `script[src="${ externalSrc } "]` ) ) {
810+ const extScript = document . createElement ( "script" ) ;
811+ extScript . setAttribute ( "src" , externalSrc ) ;
812+ document . body . appendChild ( extScript ) ;
813+ }
814+ } else if ( ! document . querySelector ( `script[src="${ externalSrc } "]` ) ) {
815+ const extScript = document . createElement ( "script" ) ;
816+ extScript . setAttribute ( "src" , externalSrc ) ;
817+ document . body . appendChild ( extScript ) ;
818+ }
778819 }
779820 } else {
780821 compScriptChunks . push (
@@ -787,7 +828,10 @@ export async function bundleToSingleHtml(
787828 runtimeCompId || compId ,
788829 authoredRootId ,
789830 )
790- : `(function(){ try { ${ scriptEl . textContent || "" } } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();` ,
831+ : wrapInlineScriptWithErrorBoundary (
832+ scriptEl . textContent || "" ,
833+ "[HyperFrames] composition script error:" ,
834+ ) ,
791835 ) ;
792836 }
793837 scriptEl . remove ( ) ;
@@ -810,8 +854,23 @@ export async function bundleToSingleHtml(
810854 for ( const scriptEl of [ ...innerDoc . querySelectorAll ( "script" ) ] ) {
811855 const externalSrc = ( scriptEl . getAttribute ( "src" ) || "" ) . trim ( ) ;
812856 if ( externalSrc ) {
813- if ( ! compExternalScriptSrcs . includes ( externalSrc ) ) {
814- compExternalScriptSrcs . push ( externalSrc ) ;
857+ if ( ! seenCompScriptSrcs . has ( externalSrc ) ) {
858+ seenCompScriptSrcs . add ( externalSrc ) ;
859+ if ( isRelativeUrl ( externalSrc ) ) {
860+ const jsPath = safePath ( projectDir , externalSrc ) ;
861+ const js = jsPath ? safeReadFile ( jsPath ) : null ;
862+ if ( js != null ) {
863+ compScriptChunks . push ( js ) ;
864+ } else if ( ! document . querySelector ( `script[src="${ externalSrc } "]` ) ) {
865+ const extScript = document . createElement ( "script" ) ;
866+ extScript . setAttribute ( "src" , externalSrc ) ;
867+ document . body . appendChild ( extScript ) ;
868+ }
869+ } else if ( ! document . querySelector ( `script[src="${ externalSrc } "]` ) ) {
870+ const extScript = document . createElement ( "script" ) ;
871+ extScript . setAttribute ( "src" , externalSrc ) ;
872+ document . body . appendChild ( extScript ) ;
873+ }
815874 }
816875 } else {
817876 compScriptChunks . push (
@@ -823,7 +882,10 @@ export async function bundleToSingleHtml(
823882 runtimeScope ,
824883 runtimeCompId || compId ,
825884 )
826- : `(function(){ try { ${ scriptEl . textContent || "" } } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();` ,
885+ : wrapInlineScriptWithErrorBoundary (
886+ scriptEl . textContent || "" ,
887+ "[HyperFrames] composition script error:" ,
888+ ) ,
827889 ) ;
828890 }
829891 scriptEl . remove ( ) ;
@@ -839,14 +901,6 @@ export async function bundleToSingleHtml(
839901
840902 // Inject external scripts from sub-compositions (e.g., Lottie CDN)
841903 // that aren't already present in the main document.
842- for ( const extSrc of compExternalScriptSrcs ) {
843- if ( ! document . querySelector ( `script[src="${ extSrc } "]` ) ) {
844- const extScript = document . createElement ( "script" ) ;
845- extScript . setAttribute ( "src" , extSrc ) ;
846- document . body . appendChild ( extScript ) ;
847- }
848- }
849-
850904 for ( const link of compExternalLinks ) {
851905 const escapedHref = link . href . replace ( / \\ / g, "\\\\" ) . replace ( / " / g, '\\"' ) ;
852906 if ( ! document . querySelector ( `link[href="${ escapedHref } "]` ) ) {
0 commit comments