Parcourir la source

Fix Live Editor for r137 (#23373)

This is a more generic fix than hardcoding checks for 'three'.
Also fixes the export function.
Greggman il y a 3 ans
Parent
commit
b44e5a661e

+ 9 - 8
manual/examples/resources/editor-settings.js

@@ -61,7 +61,7 @@ function fixSourceLinks(url, source) {
   const urlPropRE = /(url:\s*)('|")(.*?)('|")/g;
   const urlPropRE = /(url:\s*)('|")(.*?)('|")/g;
   const workerRE = /(new\s+Worker\s*\(\s*)('|")(.*?)('|")/g;
   const workerRE = /(new\s+Worker\s*\(\s*)('|")(.*?)('|")/g;
   const importScriptsRE = /(importScripts\s*\(\s*)('|")(.*?)('|")/g;
   const importScriptsRE = /(importScripts\s*\(\s*)('|")(.*?)('|")/g;
-  const moduleRE = /(import.*?)(?!'three')('|")(.*?)('|")/g;
+  const moduleRE = /(import.*?)('|")(.*?)('|")/g;
   const prefix = getPrefix(url);
   const prefix = getPrefix(url);
   const rootPrefix = getRootPrefix(url);
   const rootPrefix = getRootPrefix(url);
 
 
@@ -82,8 +82,9 @@ function fixSourceLinks(url, source) {
   function makeTaggedFDedQuotes(match, start, q1, url, q2, suffix) {
   function makeTaggedFDedQuotes(match, start, q1, url, q2, suffix) {
     return start + q1 + addPrefix(url) + q2 + suffix;
     return start + q1 + addPrefix(url) + q2 + suffix;
   }
   }
-  function makeFDedQuotes(match, start, q1, url, q2) {
-    return start + q1 + addPrefix(url) + q2;
+  function makeFDedQuotesModule(match, start, q1, url, q2) {
+    // modules require relative paths or fully qualified, otherwise they are module names
+    return `${start}${q1}${url.startsWith('.') ? addPrefix(url) : url}${q2}`;
   }
   }
   function makeArrayLinksFDed(match, prefix, arrayStr, suffix) {
   function makeArrayLinksFDed(match, prefix, arrayStr, suffix) {
     const lines = arrayStr.split(',').map((line) => {
     const lines = arrayStr.split(',').map((line) => {
@@ -105,7 +106,7 @@ function fixSourceLinks(url, source) {
   source = source.replace(importScriptsRE, makeLinkFDedQuotes);
   source = source.replace(importScriptsRE, makeLinkFDedQuotes);
   source = source.replace(loaderArrayLoadRE, makeArrayLinksFDed);
   source = source.replace(loaderArrayLoadRE, makeArrayLinksFDed);
   source = source.replace(threejsUrlRE, makeTaggedFDedQuotes);
   source = source.replace(threejsUrlRE, makeTaggedFDedQuotes);
-  source = source.replace(moduleRE, makeFDedQuotes);
+  source = source.replace(moduleRE, makeFDedQuotesModule);
 
 
   return source;
   return source;
 }
 }
@@ -130,8 +131,8 @@ let version;
 async function fixJSForCodeSite(js) {
 async function fixJSForCodeSite(js) {
   const moduleRE = /(import.*?)('|")(.*?)('|")/g;
   const moduleRE = /(import.*?)('|")(.*?)('|")/g;
 
 
-  // convert https://threejs.org/build/three.module.js -> https://cdn.skypack.dev/three@<version>
-  // convert https://threejs.org/examples/jsm/.?? -> https://cdn.skypack.dev/three@<version>/examples/jsm/.??
+  // convert https://threejs.org/build/three.module.js -> https://unpkg.com/three@<version>
+  // convert https://threejs.org/examples/jsm/.?? -> https://unpkg.com/three@<version>/examples/jsm/.??
 
 
   if (!version) {
   if (!version) {
     try {
     try {
@@ -146,10 +147,10 @@ async function fixJSForCodeSite(js) {
   function addVersion(href) {
   function addVersion(href) {
     if (href.startsWith(window.location.origin)) {
     if (href.startsWith(window.location.origin)) {
       if (href.includes('/build/three.module.js')) {
       if (href.includes('/build/three.module.js')) {
-        return `https://cdn.skypack.dev/three@${version}`;
+        return `https://unpkg.com/three@${version}`;
       } else if (href.includes('/examples/jsm/')) {
       } else if (href.includes('/examples/jsm/')) {
         const url = new URL(href);
         const url = new URL(href);
-        return `https://cdn.skypack.dev/three@${version}${url.pathname}${url.search}${url.hash}`;
+        return `https://unpkg.com/three@${version}${url.pathname}${url.search}${url.hash}`;
       }
       }
     }
     }
     return href;
     return href;

+ 0 - 3
manual/examples/resources/editor.html

@@ -190,9 +190,6 @@ button:focus {
     border-bottom: none !important;
     border-bottom: none !important;
     border-top: 5px solid #666 !important;
     border-top: 5px solid #666 !important;
 }
 }
-.button-export {
-	display: none; /* TODO: Fix export with import maps */
-}
 .button-result {
 .button-result {
     margin-left: 2em !important;
     margin-left: 2em !important;
 }
 }

+ 64 - 10
manual/examples/resources/editor.js

@@ -122,6 +122,26 @@ const htmlParts = {
   },
   },
 };
 };
 
 
+function getRootPrefix(url) {
+  const u = new URL(url, window.location.href);
+  return u.origin;
+}
+
+function removeDotDotSlash(href) {
+  // assumes a well formed URL. In other words: 'https://..//foo.html" is a bad URL and this code would fail.
+  const url = new URL(href, window.location.href);
+  const parts = url.pathname.split('/');
+  for (;;) {
+    const dotDotNdx = parts.indexOf('..');
+    if (dotDotNdx < 0) {
+      break;
+    }
+    parts.splice(dotDotNdx - 1, 2);
+  }
+  url.pathname = parts.join('/');
+  return url.toString();
+}
+
 function forEachHTMLPart(fn) {
 function forEachHTMLPart(fn) {
   Object.keys(htmlParts).forEach(function(name, ndx) {
   Object.keys(htmlParts).forEach(function(name, ndx) {
     const info = htmlParts[name];
     const info = htmlParts[name];
@@ -201,10 +221,17 @@ async function getWorkerScripts(text, baseUrl, scriptInfos = {}) {
 
 
     return `${prefix}${quote}${fqURL}${quote}`;
     return `${prefix}${quote}${fqURL}${quote}`;
   }
   }
+  function replaceWithUUIDModule(match, prefix, quote, url) {
+    // modules are either relative, fully qualified, or a module name
+    // Skip it if it's a module name
+    return (url.startsWith('.') || url.includes('://'))
+        ? replaceWithUUID(match, prefix, quote, url)
+        : match.toString();
+  }
 
 
   text = text.replace(workerRE, replaceWithUUID);
   text = text.replace(workerRE, replaceWithUUID);
   text = text.replace(importScriptsRE, replaceWithUUID);
   text = text.replace(importScriptsRE, replaceWithUUID);
-  text = text.replace(importRE, replaceWithUUID);
+  text = text.replace(importRE, replaceWithUUIDModule);
 
 
   await Promise.all(newScripts.map((url) => {
   await Promise.all(newScripts.map((url) => {
     return getScript(url, scriptInfos);
     return getScript(url, scriptInfos);
@@ -233,8 +260,8 @@ async function parseHTML(url, html) {
   const bodyRE = /<body>([^]*?)<\/body>/i;
   const bodyRE = /<body>([^]*?)<\/body>/i;
   const inlineScriptRE = /<script>([^]*?)<\/script>/i;
   const inlineScriptRE = /<script>([^]*?)<\/script>/i;
   const inlineModuleScriptRE = /<script type="module">([^]*?)<\/script>/i;
   const inlineModuleScriptRE = /<script type="module">([^]*?)<\/script>/i;
-  const externalScriptRE = /(<!--(?:(?!-->)[\s\S])*?-->\n){0,1}<script\s+(type="module"\s+)?src\s*=\s*"(.*?)"\s*>\s*<\/script>/ig;
-  const dataScriptRE = /(<!--(?:(?!-->)[\s\S])*?-->\n){0,1}<script(.*?id=".*?)>([^]*?)<\/script>/ig;
+  const externalScriptRE = /(<!--(?:(?!-->)[\s\S])*?-->\n){0,1}<script\s+([^>]*?)(type="module"\s+)?src\s*=\s*"(.*?)"(.*?)>\s*<\/script>/ig;
+  const dataScriptRE = /(<!--(?:(?!-->)[\s\S])*?-->\n){0,1}<script([^>]*?type="(?!module).*?".*?)>([^]*?)<\/script>/ig;
   const cssLinkRE = /<link ([^>]+?)>/g;
   const cssLinkRE = /<link ([^>]+?)>/g;
   const isCSSLinkRE = /type="text\/css"|rel="stylesheet"/;
   const isCSSLinkRE = /type="text\/css"|rel="stylesheet"/;
   const hrefRE = /href="([^"]+)"/;
   const hrefRE = /href="([^"]+)"/;
@@ -270,19 +297,48 @@ async function parseHTML(url, html) {
 
 
   const kScript = 'script';
   const kScript = 'script';
   const scripts = [];
   const scripts = [];
-  html = html.replace(externalScriptRE, function(p0, p1, type, p2) {
+  html = html.replace(externalScriptRE, function(p0, p1, p2, type, p3, p4) {
     p1 = p1 || '';
     p1 = p1 || '';
-    scripts.push(`${p1}<${kScript} ${safeStr(type)}src="${p2}"></${kScript}>`);
+    scripts.push(`${p1}<${kScript} ${p2}${safeStr(type)}src="${p3}"${p4}></${kScript}>`);
     return '';
     return '';
   });
   });
 
 
+  const prefix = getPrefix(url);
+  const rootPrefix = getRootPrefix(url);
+
+  function addCorrectPrefix(href) {
+    return (href.startsWith('/'))
+       ? `${rootPrefix}${href}`
+       : removeDotDotSlash((`${prefix}/${href}`).replace(/\/.\//g, '/'));
+  }
+
+  function addPrefix(url) {
+    return url.indexOf('://') < 0 && !url.startsWith('data:') && url[0] !== '?'
+        ? removeDotDotSlash(addCorrectPrefix(url))
+        : url;
+  }
+
+  const importMapRE = /type\s*=["']importmap["']/;
   const dataScripts = [];
   const dataScripts = [];
-  html = html.replace(dataScriptRE, function(p0, p1, p2, p3) {
-    p1 = p1 || '';
-    dataScripts.push(`${p1}<${kScript} ${p2}>${p3}</${kScript}>`);
+  html = html.replace(dataScriptRE, function(p0, blockComments, scriptTagAttrs, content) {
+    blockComments = blockComments || '';
+    if (importMapRE.test(scriptTagAttrs)) {
+      const imap = JSON.parse(content);
+      const imports = imap.imports;
+      if (imports) {
+        for (let [k, url] of Object.entries(imports)) {
+          if (url.indexOf('://') < 0 && !url.startsWith('data:')) {
+            imports[k] = addPrefix(url);
+          }
+        }
+      }
+      content = JSON.stringify(imap, null, '\t');
+    }
+    dataScripts.push(`${blockComments}<${kScript} ${scriptTagAttrs}>${content}</${kScript}>`);
     return '';
     return '';
   });
   });
 
 
+
   htmlParts.html.sources[0].source += dataScripts.join('\n');
   htmlParts.html.sources[0].source += dataScripts.join('\n');
   htmlParts.html.sources[0].source += scripts.join('\n');
   htmlParts.html.sources[0].source += scripts.join('\n');
 
 
@@ -296,8 +352,6 @@ async function parseHTML(url, html) {
   // query params but that only works in Firefox >:(
   // query params but that only works in Firefox >:(
   html = html.replace('</head>', '<script id="hackedparams">window.hackedParams = ${hackedParams}\n</script>\n</head>');
   html = html.replace('</head>', '<script id="hackedparams">window.hackedParams = ${hackedParams}\n</script>\n</head>');
 
 
-	html = html.replace('../../build/three.module.js', window.location.origin + '/build/three.module.js');
-
   html = extraHTMLParsing(html, htmlParts);
   html = extraHTMLParsing(html, htmlParts);
 
 
   let links = '';
   let links = '';