Skip to content

Commit 63478cd

Browse files
authored
Use document.write to write html files (codesandbox#702)
* Use document.write to write html files * Nove the handling of external resources * Tweaks * Add extra check * Revert change on irrelevant file * Add TODO * Fix external resource loading * Remove unnecessary async statement * Fix waiting for individual external resources * Add onerror listener * Add dummy script * Replace the document instead of creating new one * Use document.write based on code type * Put ext resources loading as last * Temporary fix * Solve html updating with UI * Change message to more friendly one * Fix lint * Replace resize listeners * Use shorthand property for html
1 parent 39a60b4 commit 63478cd

File tree

7 files changed

+118
-34
lines changed

7 files changed

+118
-34
lines changed

packages/app/src/app/components/CodeEditor/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
7474
render() {
7575
const props = this.props;
7676

77-
const settings = props.settings;
78-
const module = props.currentModule;
79-
const sandbox = props.sandbox;
77+
const { isModuleSynced, sandbox, currentModule: module, settings } = props;
8078
const dependencies = getDependencies(sandbox);
8179

8280
const template = getDefinition(sandbox.template);
@@ -142,6 +140,13 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
142140
bottom: 0,
143141
}}
144142
>
143+
{!isModuleSynced &&
144+
module.title === 'index.html' && (
145+
<Icons style={{ fontSize: '.875rem' }}>
146+
You may have to save this file and refresh the preview to see
147+
changes
148+
</Icons>
149+
)}
145150
{config &&
146151
(getUI(config.type) ? (
147152
<Icons>

packages/app/src/app/pages/Sandbox/Editor/Content/Preview/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ class Preview extends React.Component<Props, State> {
181181
};
182182

183183
handleModuleSyncedChange = (preview, change) => {
184-
if (change) {
184+
const settings = this.props.store.preferences.settings;
185+
if (!settings.livePreviewEnabled && change) {
185186
preview.executeCodeImmediately();
186187
}
187188
};

packages/app/src/app/pages/Sandbox/Editor/Content/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,9 @@ class EditorPreview extends React.Component<Props, State> {
403403
onInitialized={this.onInitialized}
404404
sandbox={sandbox}
405405
currentModule={currentModule}
406+
isModuleSynced={store.editor.isModuleSynced(
407+
currentModule.shortid
408+
)}
406409
width={editorWidth}
407410
height={editorHeight}
408411
settings={settings(store)}

packages/app/src/embed/components/Content/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ export default class Content extends React.PureComponent<Props, State> {
340340
<CodeEditor
341341
onInitialized={this.onCodeEditorInitialized}
342342
currentModule={currentModule || mainModule}
343+
isModuleSynced
343344
sandbox={sandbox}
344345
settings={this.getPreferences()}
345346
canSave={false}

packages/app/src/sandbox/compile.js

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import createCodeSandboxOverlay from './codesandbox-overlay';
1414
import handleExternalResources from './external-resources';
1515

1616
import defaultBoilerplates from './boilerplates/default-boilerplates';
17-
import resizeEventListener from './resize-event-listener';
17+
1818
import {
1919
getBoilerplates,
2020
evalBoilerplates,
@@ -41,8 +41,24 @@ export function getCurrentManager(): ?Manager {
4141
return manager;
4242
}
4343

44+
export function getHTMLParts(html: string) {
45+
if (html.includes('<body>')) {
46+
const bodyMatcher = /<body>([\s\S]*)<\/body>/m;
47+
const headMatcher = /<head>([\s\S]*)<\/head>/m;
48+
const body = html.match(bodyMatcher)[1];
49+
const head = html.match(headMatcher)[1];
50+
51+
return { body, head };
52+
}
53+
54+
return { head: '', body: html };
55+
}
56+
4457
let firstLoad = true;
4558
let hadError = false;
59+
let lastHeadHTML = null;
60+
let lastBodyHTML = null;
61+
let lastHeight = 0;
4662
let changedModuleCount = 0;
4763

4864
const DEPENDENCY_ALIASES = {
@@ -259,16 +275,37 @@ async function updateManager(
259275
});
260276
}
261277

262-
function initializeResizeListener() {
263-
const listener = resizeEventListener();
264-
listener.addResizeListener(document.body, () => {
278+
function getDocumentHeight() {
279+
const body = document.body;
280+
const html = document.documentElement;
281+
282+
const height = Math.max(
283+
body.scrollHeight,
284+
body.offsetHeight,
285+
html.clientHeight,
286+
html.scrollHeight,
287+
html.offsetHeight
288+
);
289+
}
290+
291+
function sendResize() {
292+
const height = getDocumentHeight();
293+
294+
if (lastHeight !== height) {
265295
if (document.body) {
266296
dispatch({
267297
type: 'resize',
268-
height: document.body.getBoundingClientRect().height,
298+
height,
269299
});
270300
}
271-
});
301+
}
302+
303+
lastHeight = height;
304+
}
305+
306+
function initializeResizeListener() {
307+
setInterval(sendResize, 5000);
308+
272309
initializedResizeListener = true;
273310
}
274311

@@ -331,7 +368,6 @@ async function compile({
331368
hadError = false;
332369

333370
actionsEnabled = hasActions;
334-
handleExternalResources(externalResources);
335371

336372
let managerModuleToTranspile = null;
337373
try {
@@ -413,7 +449,6 @@ async function compile({
413449
dispatch({ type: 'status', status: 'transpiling' });
414450

415451
await manager.verifyTreeTranspiled();
416-
await manager.preset.setup(manager);
417452
await manager.transpileModules(managerModuleToTranspile);
418453

419454
const managerTranspiledModuleToTranspile = manager.getTranspiledModule(
@@ -459,24 +494,39 @@ async function compile({
459494
}
460495
}
461496

462-
if ((!manager.webpackHMR || firstLoad) && !manager.preset.htmlDisabled) {
463-
if (!managerTranspiledModuleToTranspile.compilation || isModuleView) {
464-
const htmlModule =
465-
modules[
466-
templateDefinition
467-
.getHTMLEntries(configurations)
468-
.find(p => modules[p])
469-
];
497+
if (!manager.webpackHMR && !manager.preset.htmlDisabled) {
498+
const htmlModulePath = templateDefinition
499+
.getHTMLEntries(configurations)
500+
.find(p => modules[p]);
501+
const htmlModule = modules[htmlModulePath];
470502

471-
const html = htmlModule
503+
const { head, body } = getHTMLParts(
504+
htmlModule
472505
? htmlModule.code
473506
: template === 'vue-cli'
474507
? '<div id="app"></div>'
475-
: '<div id="root"></div>';
476-
document.body.innerHTML = html;
508+
: '<div id="root"></div>'
509+
);
510+
511+
document.body.innerHTML = body;
512+
513+
if (lastHeadHTML && lastHeadHTML !== head) {
514+
document.location.reload();
515+
}
516+
if (manager && lastBodyHTML && lastBodyHTML !== body) {
517+
manager.clearCompiledCache();
477518
}
519+
520+
lastBodyHTML = body;
521+
lastHeadHTML = head;
478522
}
479523

524+
const extDate = Date.now();
525+
await handleExternalResources(externalResources);
526+
debug('Loaded external resources in ' + (Date.now() - extDate) + 'ms');
527+
528+
await manager.preset.setup(manager);
529+
480530
const tt = Date.now();
481531
const oldHTML = document.body.innerHTML;
482532
const evalled = manager.evaluateModule(

packages/app/src/sandbox/external-resources.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ function addCSS(resource: string) {
2222
link.href = resource;
2323
link.media = 'all';
2424
head.appendChild(link);
25+
26+
return link;
2527
}
2628

2729
function addJS(resource: string) {
@@ -30,25 +32,42 @@ function addJS(resource: string) {
3032
script.async = false;
3133
script.setAttribute('id', 'external-js');
3234
document.head.appendChild(script);
35+
36+
return script;
3337
}
3438

3539
function addResource(resource: string) {
3640
const match = resource.match(/\.([^.]*)$/);
3741

38-
if (match && match[1] === 'css') {
39-
addCSS(resource);
40-
} else {
41-
addJS(resource);
42-
}
42+
const el = match && match[1] === 'css' ? addCSS(resource) : addJS(resource);
43+
44+
return new Promise(r => {
45+
el.onload = r;
46+
el.onerror = r;
47+
});
48+
}
49+
50+
function waitForLoaded() {
51+
return new Promise(resolve => {
52+
if (document.readyState !== 'complete') {
53+
window.addEventListener('load', resolve);
54+
} else {
55+
resolve();
56+
}
57+
});
4358
}
4459

4560
let cachedExternalResources = '';
4661

47-
export default function handleExternalResources(externalResources) {
62+
export default async function handleExternalResources(externalResources) {
4863
const extResString = getExternalResourcesConcatenation(externalResources);
4964
if (extResString !== cachedExternalResources) {
5065
clearExternalResources();
51-
externalResources.forEach(addResource);
66+
await Promise.all(externalResources.map(addResource));
5267
cachedExternalResources = extResString;
68+
69+
return waitForLoaded();
5370
}
71+
72+
return Promise.resolve();
5473
}

packages/app/src/sandbox/index.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ requirePolyfills().then(() => {
5858
history.back();
5959
} else if (data.type === 'urlforward') {
6060
history.forward();
61+
} else if (data.type === 'refresh') {
62+
document.location.reload();
6163
} else if (data.type === 'evaluate') {
6264
let result = null;
6365
let error = false;
@@ -110,11 +112,14 @@ requirePolyfills().then(() => {
110112
}
111113
}
112114

113-
listen(handleMessage);
115+
if (!isStandalone) {
116+
listen(handleMessage);
114117

115-
sendReady();
116-
setupHistoryListeners();
117-
setupConsole();
118+
sendReady();
119+
120+
setupHistoryListeners();
121+
setupConsole();
122+
}
118123

119124
if (process.env.NODE_ENV === 'test' || isStandalone) {
120125
// We need to fetch the sandbox ourselves...

0 commit comments

Comments
 (0)