Skip to content

Commit

Permalink
Fix Live Editor for r137
Browse files Browse the repository at this point in the history
This is a more generic fix than hardcoding checks for 'three'.
Also fixes the export function.
  • Loading branch information
greggman committed Jan 29, 2022
1 parent 44d091b commit 7b3f5bc
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 21 deletions.
17 changes: 9 additions & 8 deletions manual/examples/resources/editor-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function fixSourceLinks(url, source) {
const urlPropRE = /(url:\s*)('|")(.*?)('|")/g;
const workerRE = /(new\s+Worker\s*\(\s*)('|")(.*?)('|")/g;
const importScriptsRE = /(importScripts\s*\(\s*)('|")(.*?)('|")/g;
const moduleRE = /(import.*?)(?!'three')('|")(.*?)('|")/g;
const moduleRE = /(import.*?)('|")(.*?)('|")/g;
const prefix = getPrefix(url);
const rootPrefix = getRootPrefix(url);

Expand All @@ -82,8 +82,9 @@ function fixSourceLinks(url, source) {
function makeTaggedFDedQuotes(match, start, q1, 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) {
const lines = arrayStr.split(',').map((line) => {
Expand All @@ -105,7 +106,7 @@ function fixSourceLinks(url, source) {
source = source.replace(importScriptsRE, makeLinkFDedQuotes);
source = source.replace(loaderArrayLoadRE, makeArrayLinksFDed);
source = source.replace(threejsUrlRE, makeTaggedFDedQuotes);
source = source.replace(moduleRE, makeFDedQuotes);
source = source.replace(moduleRE, makeFDedQuotesModule);

return source;
}
Expand All @@ -130,8 +131,8 @@ let version;
async function fixJSForCodeSite(js) {
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) {
try {
Expand All @@ -146,10 +147,10 @@ async function fixJSForCodeSite(js) {
function addVersion(href) {
if (href.startsWith(window.location.origin)) {
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/')) {
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;
Expand Down
3 changes: 0 additions & 3 deletions manual/examples/resources/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,6 @@
border-bottom: none !important;
border-top: 5px solid #666 !important;
}
.button-export {
display: none; /* TODO: Fix export with import maps */
}
.button-result {
margin-left: 2em !important;
}
Expand Down
74 changes: 64 additions & 10 deletions manual/examples/resources/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Object.keys(htmlParts).forEach(function(name, ndx) {
const info = htmlParts[name];
Expand Down Expand Up @@ -201,10 +221,17 @@ async function getWorkerScripts(text, baseUrl, scriptInfos = {}) {

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(importScriptsRE, replaceWithUUID);
text = text.replace(importRE, replaceWithUUID);
text = text.replace(importRE, replaceWithUUIDModule);

await Promise.all(newScripts.map((url) => {
return getScript(url, scriptInfos);
Expand Down Expand Up @@ -233,8 +260,8 @@ async function parseHTML(url, html) {
const bodyRE = /<body>([^]*?)<\/body>/i;
const inlineScriptRE = /<script>([^]*?)<\/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 isCSSLinkRE = /type="text\/css"|rel="stylesheet"/;
const hrefRE = /href="([^"]+)"/;
Expand Down Expand Up @@ -270,19 +297,48 @@ async function parseHTML(url, html) {

const kScript = 'script';
const scripts = [];
html = html.replace(externalScriptRE, function(p0, p1, type, p2) {
html = html.replace(externalScriptRE, function(p0, p1, p2, type, p3, p4) {
p1 = p1 || '';
scripts.push(`${p1}<${kScript} ${safeStr(type)}src="${p2}"></${kScript}>`);
scripts.push(`${p1}<${kScript} ${p2}${safeStr(type)}src="${p3}"${p4}></${kScript}>`);
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 = [];
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 '';
});


htmlParts.html.sources[0].source += dataScripts.join('\n');
htmlParts.html.sources[0].source += scripts.join('\n');

Expand All @@ -296,8 +352,6 @@ async function parseHTML(url, html) {
// 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('../../build/three.module.js', window.location.origin + '/build/three.module.js');

html = extraHTMLParsing(html, htmlParts);

let links = '';
Expand Down

0 comments on commit 7b3f5bc

Please sign in to comment.