Repository: pyscript/pyscript
File: pyscriptjs/src/utils.ts (TypeScript)

Code before:
}

export function showWarning(msg: string, messageType: "text" | "html" = "text"): void {
    _createAlertBanner(msg, "warning", messageType);
}

export function handleFetchError(e: Error, singleFile: string) {
    // XXX: inspecting the error message to understand what happened is very
    // fragile. We need a better solution.
    let errorContent: string;
    if (e.message.includes('Failed to fetch')) {
        errorContent = `PyScript: Access to local files
        (using "Paths:" in &lt;py-config&gt;)
        is not available when directly opening a HTML file;
        you must use a webserver to serve the additional files.
        See <a style="text-decoration: underline;" href="https://github.com/pyscript/pyscript/issues/257#issuecomment-1119595062">this reference</a>
        on starting a simple webserver with Python.`;
    } else if (e.message.includes('404')) {
        errorContent =
            `PyScript: Loading from file <u>` +
            singleFile +
            `</u> failed with error 404 (File not Found). Are your filename and path are correct?`;
    } else {
        errorContent = `PyScript encountered an error while loading from file: ${e.message}`;
    }
    throw new UserError(UserError.ErrorCode.FETCH_ERROR, errorContent, "html");
}

export function readTextFromPath(path: string) {
    const request = new XMLHttpRequest();
    request.open('GET', path, false);
    request.send();
    const returnValue = request.responseText;

    return returnValue;
}

export function inJest(): boolean {
    return typeof process === 'object' && process.env.JEST_WORKER_ID !== undefined;
}

export function globalExport(name: string, obj: object) {
    // attach the given object to the global object, so that it is globally
    // visible everywhere. Should be used very sparingly!

    globalThis[name] = obj;
}

export function getAttribute(el: Element, attr: string): string | null {
    if (el.hasAttribute(attr)) {
        const value = el.getAttribute(attr);

Review comment (bug):
[follow-up]
this seems to be related to the comment which I made above about FetchUserError & co.
I think that we can improve this messy logic: instead of having a "catch-all" function which tries to interpret the error after the fact, we could probably be proactive: every call to fetchRobust(userError=True) (pseudocode) should also indicate an user-friendly explanation.

For example, if we fetch something from the [[fetch]] config, saying Check your filename might be useful. But if we fail to fetch some internal file (e.g. the pyodide stdlib), then saying Check your filename is just confusing to the user. But again, this sounds like a topic for a separate discussion

Code after:
}

export function showWarning(msg: string, messageType: "text" | "html" = "text"): void {
    _createAlertBanner(msg, "warning", messageType);
}

export function handleFetchError(e: Error, singleFile: string) {
    // XXX: inspecting the error message to understand what happened is very
    // fragile. We need a better solution.
    let errorContent: string;
    if (e.message.includes('Failed to fetch')) {
        errorContent = `PyScript: Access to local files
        (using "Paths:" in &lt;py-config&gt;)
        is not available when directly opening a HTML file;
        you must use a webserver to serve the additional files.
        See <a style="text-decoration: underline;" href="https://github.com/pyscript/pyscript/issues/257#issuecomment-1119595062">this reference</a>
        on starting a simple webserver with Python.`;
    } else if (e.message.includes('404')) {
        errorContent =
            `PyScript: Loading from file <u>` +
            singleFile +
            `</u> failed with error 404 (File not Found). Are your filename and path are correct?`;
    } else {
        errorContent = `PyScript encountered an error while loading from file: ${e.message}`;
    }
    throw new UserError(ErrorCode.FETCH_ERROR, errorContent, "html");
}

export function readTextFromPath(path: string) {
    const request = new XMLHttpRequest();
    request.open('GET', path, false);
    request.send();
    const returnValue = request.responseText;

    return returnValue;
}

export function inJest(): boolean {
    return typeof process === 'object' && process.env.JEST_WORKER_ID !== undefined;
}

export function globalExport(name: string, obj: object) {
    // attach the given object to the global object, so that it is globally
    // visible everywhere. Should be used very sparingly!

    globalThis[name] = obj;
}

export function getAttribute(el: Element, attr: string): string | null {
    if (el.hasAttribute(attr)) {
        const value = el.getAttribute(attr);