Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions packages/php-wasm/progress/src/lib/emscripten-download-monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,12 @@ export function cloneStreamMonitorProgress(
);
}

return new ReadableStream({
let closed = false;
const monitoredStream = new ReadableStream({
async start(controller) {
if (!stream) {
controller.close();
closed = true;
return;
}
const reader = stream.getReader();
Expand All @@ -178,19 +180,29 @@ export function cloneStreamMonitorProgress(
if (done) {
notify(loaded, loaded);
controller.close();
closed = true;
break;
} else {
notify(loaded, total);
controller.enqueue(value);
if (!closed) {
controller.enqueue(value);
}
}
} catch (e) {
logger.error({ e });
controller.error(e);
debugger;
try {
console.log(controller);
logger.error({ e });
console.dir(e);
controller.close();
} catch (e) {}
// controller.error(e);
break;
}
}
},
});
return monitoredStream;
}

export type DownloadProgressCallback = (progress: DownloadProgress) => void;
24 changes: 21 additions & 3 deletions packages/playground/blueprints/src/lib/resolve-remote-blueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,23 @@ export async function resolveRemoteBlueprint(
credentials: 'omit',
});
if (!response.ok) {
throw new Error(`Failed to fetch blueprint from ${url}`);
const statusText = response.statusText?.trim();
const statusSummary = statusText
? `${response.status} ${statusText}`
: `${response.status}`;
const error = new Error(
response.status
? `Failed to fetch the Blueprint. The server responded with HTTP ${statusSummary}.`
: 'Failed to fetch the Blueprint.'
) as Error & {
status?: number;
statusText?: string;
url?: string;
};
error.status = response.status;
error.statusText = response.statusText;
error.url = url;
throw error;
}
const blueprintBytes = await response.arrayBuffer();
try {
Expand All @@ -41,9 +57,11 @@ export async function resolveRemoteBlueprint(
if (await looksLikeZipFile(blueprintBytes)) {
return ZipFilesystem.fromArrayBuffer(blueprintBytes);
}
throw new Error(
const error = new Error(
`Blueprint file at ${url} is neither a valid JSON nor a ZIP file.`
);
) as Error & { url?: string };
error.url = url;
throw error;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import { redirectTo } from '../../lib/state/url/router';
import { logger } from '@php-wasm/logger';
import { usePrevious } from '../../lib/hooks/use-previous';
import { modalSlugs } from '../layout';
import { setActiveModal } from '../../lib/state/redux/slice-ui';
import {
setActiveModal,
setActiveSiteError,
} from '../../lib/state/redux/slice-ui';
import { selectClientBySiteSlug } from '../../lib/state/redux/slice-clients';
import { randomSiteName } from '../../lib/state/redux/random-site-name';

Expand Down Expand Up @@ -158,8 +161,18 @@ async function createNewTemporarySite(
const siteName = requestedSiteSlug
? deriveSiteNameFromSlug(requestedSiteSlug)
: randomSiteName();
const newSiteInfo = await dispatch(
const { site, blueprintResolutionFailed } = await dispatch(
setTemporarySiteSpec(siteName, new URL(window.location.href))
);
await dispatch(setActiveSite(newSiteInfo.slug));
await dispatch(setActiveSite(site.slug));
if (blueprintResolutionFailed) {
dispatch(
setActiveSiteError({
error: 'blueprint-resolution-failed',
context: {
blueprintResolution: blueprintResolutionFailed,
},
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@ import css from './style.module.css';
import BrowserChrome from '../browser-chrome';
import {
selectActiveSiteError,
selectActiveSiteErrorContext,
useActiveSite,
useAppDispatch,
useAppSelector,
} from '../../lib/state/redux/store';
import { removeClientInfo } from '../../lib/state/redux/slice-clients';
import { bootSiteClient } from '../../lib/state/redux/boot-site-client';
import type { SiteError } from '../../lib/state/redux/slice-ui';
import type {
ActiveSiteErrorContext,
SiteError,
} from '../../lib/state/redux/slice-ui';
import { Button, Spinner } from '@wordpress/components';
import {
removeSite,
selectSiteBySlug,
selectSitesLoaded,
selectTemporarySites,
DEFAULT_WELCOME_BLUEPRINT_URL,
} from '../../lib/state/redux/slice-sites';
import classNames from 'classnames';

Expand Down Expand Up @@ -202,12 +207,17 @@ export const JustViewport = function JustViewport({
}, [siteSlug, iframeRef, runtimeConfigString]);

const error = useAppSelector(selectActiveSiteError);
const errorContext = useAppSelector(selectActiveSiteErrorContext);

if (error) {
return (
<div className={css.siteError}>
<div className={css.siteErrorContent}>
<SiteErrorMessage error={error} siteSlug={siteSlug} />
<SiteErrorMessage
error={error}
siteSlug={siteSlug}
errorContext={errorContext}
/>
</div>
</div>
);
Expand All @@ -226,11 +236,88 @@ export const JustViewport = function JustViewport({
function SiteErrorMessage({
error,
siteSlug,
errorContext,
}: {
error: SiteError;
siteSlug: string;
errorContext?: ActiveSiteErrorContext;
}) {
const dispatch = useAppDispatch();
if (error === 'blueprint-resolution-failed') {
const blueprintError = errorContext?.blueprintResolution;
const attemptedUrl =
blueprintError?.attemptedUrl || DEFAULT_WELCOME_BLUEPRINT_URL;
const rawStatusText =
typeof blueprintError?.statusText === 'string'
? blueprintError.statusText.trim()
: undefined;
const statusSummary =
typeof blueprintError?.httpStatus === 'number'
? `HTTP ${blueprintError.httpStatus}${
rawStatusText ? ` ${rawStatusText}` : ''
}`
: rawStatusText;
const trimmedMessage =
typeof blueprintError?.message === 'string'
? blueprintError.message.trim()
: undefined;
const shouldShowMessage =
trimmedMessage &&
trimmedMessage.length > 0 &&
trimmedMessage !== statusSummary &&
trimmedMessage !== attemptedUrl;
return (
<>
<h1>We couldn't load that Blueprint</h1>
<p>
WordPress Playground couldn't load the Blueprint below. The
file might be unavailable or invalid.
</p>
{attemptedUrl ? (
<p className={css.blueprintUrlWrapper}>
<a
className={css.blueprintUrl}
target="_blank"
rel="noopener noreferrer"
href={attemptedUrl}
>
{attemptedUrl}
</a>
</p>
) : null}
{statusSummary ? (
<p className={css.blueprintStatus}>{statusSummary}</p>
) : null}
{shouldShowMessage ? <p>{trimmedMessage}</p> : null}
<p>
Reload without a Blueprint to start with a blank WordPress
site.
</p>
<Button
className={css.actionButton}
variant="primary"
onClick={() => {
const url = new URL(window.location.href);
url.searchParams.delete('blueprint-url');
url.searchParams.delete('import-wxr');
url.searchParams.delete('import-content');
url.searchParams.delete('import-site');
url.searchParams.delete('plugin');
url.searchParams.delete('theme');
url.searchParams.delete('php');
url.searchParams.delete('wp');
url.searchParams.delete('language');
url.searchParams.delete('networking');
url.searchParams.delete('site-slug');
url.hash = '';
window.location.href = url.toString();
}}
>
reload without a Blueprint
</Button>
</>
);
}
if (
error === 'directory-handle-not-found-in-indexeddb' ||
error === 'directory-handle-permission-denied'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@
}
}

.blueprint-url-wrapper {
margin-bottom: 15px;
word-break: break-word;
overflow-wrap: anywhere;
}

.blueprint-url {
display: inline-block;
word-break: break-word;
overflow-wrap: anywhere;
}

.blueprintStatus {
font-weight: 600;
margin-bottom: 15px;
}

.hidden {
display: none;
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export function bootSiteClient(
} catch (e) {
logger.error(e);
dispatch(
setActiveSiteError(
'directory-handle-not-found-in-indexeddb'
)
setActiveSiteError({
error: 'directory-handle-not-found-in-indexeddb',
})
);
return;
}
Expand All @@ -82,13 +82,17 @@ export function bootSiteClient(
logger.error(e);
if (e instanceof DOMException && e.name === 'NotFoundError') {
dispatch(
setActiveSiteError(
'directory-handle-not-found-in-indexeddb'
)
setActiveSiteError({
error: 'directory-handle-not-found-in-indexeddb',
})
);
return;
}
dispatch(setActiveSiteError('directory-handle-unknown-error'));
dispatch(
setActiveSiteError({
error: 'directory-handle-unknown-error',
})
);
return;
}
}
Expand Down Expand Up @@ -197,9 +201,11 @@ export function bootSiteClient(
(e as any).name === 'ArtifactExpiredError' ||
(e as any).originalErrorClassName === 'ArtifactExpiredError'
) {
dispatch(setActiveSiteError('github-artifact-expired'));
dispatch(
setActiveSiteError({ error: 'github-artifact-expired' })
);
} else {
dispatch(setActiveSiteError('site-boot-failed'));
dispatch(setActiveSiteError({ error: 'site-boot-failed' }));
dispatch(setActiveModal(modalSlugs.ERROR_REPORT));
}
return;
Expand Down
Loading
Loading