@@ -122,6 +122,26 @@ const htmlParts = {
122122 } ,
123123} ;
124124
125+ function getRootPrefix ( url ) {
126+ const u = new URL ( url , window . location . href ) ;
127+ return u . origin ;
128+ }
129+
130+ function removeDotDotSlash ( href ) {
131+ // assumes a well formed URL. In other words: 'https://..//foo.html" is a bad URL and this code would fail.
132+ const url = new URL ( href , window . location . href ) ;
133+ const parts = url . pathname . split ( '/' ) ;
134+ for ( ; ; ) {
135+ const dotDotNdx = parts . indexOf ( '..' ) ;
136+ if ( dotDotNdx < 0 ) {
137+ break ;
138+ }
139+ parts . splice ( dotDotNdx - 1 , 2 ) ;
140+ }
141+ url . pathname = parts . join ( '/' ) ;
142+ return url . toString ( ) ;
143+ }
144+
125145function forEachHTMLPart ( fn ) {
126146 Object . keys ( htmlParts ) . forEach ( function ( name , ndx ) {
127147 const info = htmlParts [ name ] ;
@@ -201,10 +221,17 @@ async function getWorkerScripts(text, baseUrl, scriptInfos = {}) {
201221
202222 return `${ prefix } ${ quote } ${ fqURL } ${ quote } ` ;
203223 }
224+ function replaceWithUUIDModule ( match , prefix , quote , url ) {
225+ // modules are either relative, fully qualified, or a module name
226+ // Skip it if it's a module name
227+ return ( url . startsWith ( '.' ) || url . includes ( '://' ) )
228+ ? replaceWithUUID ( match , prefix , quote , url )
229+ : match . toString ( ) ;
230+ }
204231
205232 text = text . replace ( workerRE , replaceWithUUID ) ;
206233 text = text . replace ( importScriptsRE , replaceWithUUID ) ;
207- text = text . replace ( importRE , replaceWithUUID ) ;
234+ text = text . replace ( importRE , replaceWithUUIDModule ) ;
208235
209236 await Promise . all ( newScripts . map ( ( url ) => {
210237 return getScript ( url , scriptInfos ) ;
@@ -233,8 +260,8 @@ async function parseHTML(url, html) {
233260 const bodyRE = / < b o d y > ( [ ^ ] * ?) < \/ b o d y > / i;
234261 const inlineScriptRE = / < s c r i p t > ( [ ^ ] * ?) < \/ s c r i p t > / i;
235262 const inlineModuleScriptRE = / < s c r i p t t y p e = " m o d u l e " > ( [ ^ ] * ?) < \/ s c r i p t > / i;
236- const externalScriptRE = / ( < ! - - (?: (? ! - - > ) [ \s \S ] ) * ?- - > \n ) { 0 , 1 } < s c r i p t \s + ( t y p e = " m o d u l e " \s + ) ? s r c \s * = \s * " ( .* ?) " \s * > \s * < \/ s c r i p t > / ig;
237- const dataScriptRE = / ( < ! - - (?: (? ! - - > ) [ \s \S ] ) * ?- - > \n ) { 0 , 1 } < s c r i p t ( . * ? i d = " .* ?) > ( [ ^ ] * ?) < \/ s c r i p t > / ig;
263+ const externalScriptRE = / ( < ! - - (?: (? ! - - > ) [ \s \S ] ) * ?- - > \n ) { 0 , 1 } < s c r i p t \s + ( [ ^ > ] * ? ) ( t y p e = " m o d u l e " \s + ) ? s r c \s * = \s * " ( .* ?) " ( . * ? ) > \s * < \/ s c r i p t > / ig;
264+ const dataScriptRE = / ( < ! - - (?: (? ! - - > ) [ \s \S ] ) * ?- - > \n ) { 0 , 1 } < s c r i p t ( [ ^ > ] * ? t y p e = " (? ! m o d u l e ) . * ? " .* ?) > ( [ ^ ] * ?) < \/ s c r i p t > / ig;
238265 const cssLinkRE = / < l i n k ( [ ^ > ] + ?) > / g;
239266 const isCSSLinkRE = / t y p e = " t e x t \/ c s s " | r e l = " s t y l e s h e e t " / ;
240267 const hrefRE = / h r e f = " ( [ ^ " ] + ) " / ;
@@ -270,19 +297,48 @@ async function parseHTML(url, html) {
270297
271298 const kScript = 'script' ;
272299 const scripts = [ ] ;
273- html = html . replace ( externalScriptRE , function ( p0 , p1 , type , p2 ) {
300+ html = html . replace ( externalScriptRE , function ( p0 , p1 , p2 , type , p3 , p4 ) {
274301 p1 = p1 || '' ;
275- scripts . push ( `${ p1 } <${ kScript } ${ safeStr ( type ) } src="${ p2 } " ></${ kScript } >` ) ;
302+ scripts . push ( `${ p1 } <${ kScript } ${ p2 } ${ safeStr ( type ) } src="${ p3 } " ${ p4 } ></${ kScript } >` ) ;
276303 return '' ;
277304 } ) ;
278305
306+ const prefix = getPrefix ( url ) ;
307+ const rootPrefix = getRootPrefix ( url ) ;
308+
309+ function addCorrectPrefix ( href ) {
310+ return ( href . startsWith ( '/' ) )
311+ ? `${ rootPrefix } ${ href } `
312+ : removeDotDotSlash ( ( `${ prefix } /${ href } ` ) . replace ( / \/ .\/ / g, '/' ) ) ;
313+ }
314+
315+ function addPrefix ( url ) {
316+ return url . indexOf ( '://' ) < 0 && ! url . startsWith ( 'data:' ) && url [ 0 ] !== '?'
317+ ? removeDotDotSlash ( addCorrectPrefix ( url ) )
318+ : url ;
319+ }
320+
321+ const importMapRE = / t y p e \s * = [ " ' ] i m p o r t m a p [ " ' ] / ;
279322 const dataScripts = [ ] ;
280- html = html . replace ( dataScriptRE , function ( p0 , p1 , p2 , p3 ) {
281- p1 = p1 || '' ;
282- dataScripts . push ( `${ p1 } <${ kScript } ${ p2 } >${ p3 } </${ kScript } >` ) ;
323+ html = html . replace ( dataScriptRE , function ( p0 , blockComments , scriptTagAttrs , content ) {
324+ blockComments = blockComments || '' ;
325+ if ( importMapRE . test ( scriptTagAttrs ) ) {
326+ const imap = JSON . parse ( content ) ;
327+ const imports = imap . imports ;
328+ if ( imports ) {
329+ for ( let [ k , url ] of Object . entries ( imports ) ) {
330+ if ( url . indexOf ( '://' ) < 0 && ! url . startsWith ( 'data:' ) ) {
331+ imports [ k ] = addPrefix ( url ) ;
332+ }
333+ }
334+ }
335+ content = JSON . stringify ( imap , null , '\t' ) ;
336+ }
337+ dataScripts . push ( `${ blockComments } <${ kScript } ${ scriptTagAttrs } >${ content } </${ kScript } >` ) ;
283338 return '' ;
284339 } ) ;
285340
341+
286342 htmlParts . html . sources [ 0 ] . source += dataScripts . join ( '\n' ) ;
287343 htmlParts . html . sources [ 0 ] . source += scripts . join ( '\n' ) ;
288344
@@ -296,8 +352,6 @@ async function parseHTML(url, html) {
296352 // query params but that only works in Firefox >:(
297353 html = html . replace ( '</head>' , '<script id="hackedparams">window.hackedParams = ${hackedParams}\n</script>\n</head>' ) ;
298354
299- html = html . replace ( '../../build/three.module.js' , window . location . origin + '/build/three.module.js' ) ;
300-
301355 html = extraHTMLParsing ( html , htmlParts ) ;
302356
303357 let links = '' ;
0 commit comments