Skip to content

Commit b44e5a6

Browse files
authored
Fix Live Editor for r137 (#23373)
This is a more generic fix than hardcoding checks for 'three'. Also fixes the export function.
1 parent 3331cf4 commit b44e5a6

File tree

3 files changed

+73
-21
lines changed

3 files changed

+73
-21
lines changed

manual/examples/resources/editor-settings.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function fixSourceLinks(url, source) {
6161
const urlPropRE = /(url:\s*)('|")(.*?)('|")/g;
6262
const workerRE = /(new\s+Worker\s*\(\s*)('|")(.*?)('|")/g;
6363
const importScriptsRE = /(importScripts\s*\(\s*)('|")(.*?)('|")/g;
64-
const moduleRE = /(import.*?)(?!'three')('|")(.*?)('|")/g;
64+
const moduleRE = /(import.*?)('|")(.*?)('|")/g;
6565
const prefix = getPrefix(url);
6666
const rootPrefix = getRootPrefix(url);
6767

@@ -82,8 +82,9 @@ function fixSourceLinks(url, source) {
8282
function makeTaggedFDedQuotes(match, start, q1, url, q2, suffix) {
8383
return start + q1 + addPrefix(url) + q2 + suffix;
8484
}
85-
function makeFDedQuotes(match, start, q1, url, q2) {
86-
return start + q1 + addPrefix(url) + q2;
85+
function makeFDedQuotesModule(match, start, q1, url, q2) {
86+
// modules require relative paths or fully qualified, otherwise they are module names
87+
return `${start}${q1}${url.startsWith('.') ? addPrefix(url) : url}${q2}`;
8788
}
8889
function makeArrayLinksFDed(match, prefix, arrayStr, suffix) {
8990
const lines = arrayStr.split(',').map((line) => {
@@ -105,7 +106,7 @@ function fixSourceLinks(url, source) {
105106
source = source.replace(importScriptsRE, makeLinkFDedQuotes);
106107
source = source.replace(loaderArrayLoadRE, makeArrayLinksFDed);
107108
source = source.replace(threejsUrlRE, makeTaggedFDedQuotes);
108-
source = source.replace(moduleRE, makeFDedQuotes);
109+
source = source.replace(moduleRE, makeFDedQuotesModule);
109110

110111
return source;
111112
}
@@ -130,8 +131,8 @@ let version;
130131
async function fixJSForCodeSite(js) {
131132
const moduleRE = /(import.*?)('|")(.*?)('|")/g;
132133

133-
// convert https://threejs.org/build/three.module.js -> https://cdn.skypack.dev/three@<version>
134-
// convert https://threejs.org/examples/jsm/.?? -> https://cdn.skypack.dev/three@<version>/examples/jsm/.??
134+
// convert https://threejs.org/build/three.module.js -> https://unpkg.com/three@<version>
135+
// convert https://threejs.org/examples/jsm/.?? -> https://unpkg.com/three@<version>/examples/jsm/.??
135136

136137
if (!version) {
137138
try {
@@ -146,10 +147,10 @@ async function fixJSForCodeSite(js) {
146147
function addVersion(href) {
147148
if (href.startsWith(window.location.origin)) {
148149
if (href.includes('/build/three.module.js')) {
149-
return `https://cdn.skypack.dev/three@${version}`;
150+
return `https://unpkg.com/three@${version}`;
150151
} else if (href.includes('/examples/jsm/')) {
151152
const url = new URL(href);
152-
return `https://cdn.skypack.dev/three@${version}${url.pathname}${url.search}${url.hash}`;
153+
return `https://unpkg.com/three@${version}${url.pathname}${url.search}${url.hash}`;
153154
}
154155
}
155156
return href;

manual/examples/resources/editor.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,6 @@
190190
border-bottom: none !important;
191191
border-top: 5px solid #666 !important;
192192
}
193-
.button-export {
194-
display: none; /* TODO: Fix export with import maps */
195-
}
196193
.button-result {
197194
margin-left: 2em !important;
198195
}

manual/examples/resources/editor.js

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
125145
function 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 = /<body>([^]*?)<\/body>/i;
234261
const inlineScriptRE = /<script>([^]*?)<\/script>/i;
235262
const inlineModuleScriptRE = /<script type="module">([^]*?)<\/script>/i;
236-
const externalScriptRE = /(<!--(?:(?!-->)[\s\S])*?-->\n){0,1}<script\s+(type="module"\s+)?src\s*=\s*"(.*?)"\s*>\s*<\/script>/ig;
237-
const dataScriptRE = /(<!--(?:(?!-->)[\s\S])*?-->\n){0,1}<script(.*?id=".*?)>([^]*?)<\/script>/ig;
263+
const externalScriptRE = /(<!--(?:(?!-->)[\s\S])*?-->\n){0,1}<script\s+([^>]*?)(type="module"\s+)?src\s*=\s*"(.*?)"(.*?)>\s*<\/script>/ig;
264+
const dataScriptRE = /(<!--(?:(?!-->)[\s\S])*?-->\n){0,1}<script([^>]*?type="(?!module).*?".*?)>([^]*?)<\/script>/ig;
238265
const cssLinkRE = /<link ([^>]+?)>/g;
239266
const isCSSLinkRE = /type="text\/css"|rel="stylesheet"/;
240267
const hrefRE = /href="([^"]+)"/;
@@ -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 = /type\s*=["']importmap["']/;
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

Comments
 (0)