caiiiycuk / js-dos

The best API for running dos programs in browser
https://js-dos.com
1k stars 123 forks source link

Building on macos fails #277

Closed NaLiJa closed 1 year ago

NaLiJa commented 1 year ago

(Thanks for the your fantastic work!) Trying to build the project constantly fails on Macos Big Sur with the follwing error. Unfortunately I wasn't able to fix this: Perhps any idea what is going wrong?

yarn run v1.22.19 $ /Users/Shared/Developer/js-dos/node_modules/.bin/gulp js [21:15:04] Requiring external module ts-node/register [21:15:06] Using gulpfile /Users/Shared/Developer/js-dos/gulpfile.ts [21:15:06] Starting 'js'... [21:15:06] Starting 'playerJs'... [21:15:09] 'playerJs' errored after 2.75 s [21:15:09] Error: Parsing file /Users/Shared/Developer/js-dos/src/components/sidebar/token/token.ts: Unexpected token (49:23) at Deps.parseDeps (/Users/Shared/Developer/js-dos/node_modules/module-deps/index.js:519:15) at getDeps (/Users/Shared/Developer/js-dos/node_modules/module-deps/index.js:447:44) at /Users/Shared/Developer/js-dos/node_modules/module-deps/index.js:430:38 at ConcatStream. (/Users/Shared/Developer/js-dos/node_modules/concat-stream/index.js:37:43) at ConcatStream.emit (node:events:525:35) at ConcatStream.emit (node:domain:552:15) at finishMaybe (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_writable.js:630:14) at endWritable (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_writable.js:638:3) at ConcatStream.Writable.end (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_writable.js:594:22) at DuplexWrapper.onend (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_readable.js:577:10) at Object.onceWrapper (node:events:627:28) at DuplexWrapper.emit (node:events:525:35) at DuplexWrapper.emit (node:domain:552:15) at endReadableNT (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_readable.js:1010:12) at processTicksAndRejections (node:internal/process/task_queues:82:21) [21:15:09] 'js' errored after 2.76 s error Command failed with exit code 1.

This is node v19.9.0, yarn 1.22.19, gulp at CLI version: 2.3.0, Local version: 4.0.2

Any hints appreciated!

caiiiycuk commented 1 year ago

Hi, maybe you use wrong tsc? Did you run yarn run gulp?

NaLiJa commented 1 year ago

Hi, thanks a lot for trying to help me! Yes, I did use yarn run gulp, even just ran it again, just to make sure. But the error persists. Even downgraded my node to node 14.21, so I could try to run the commands from your automatic build script, but the error persists. Here is the complete output of my latest try.

Sunlight:js-dos stefan$ yarn yarn install v1.22.19 warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json. [1/4] 🔍 Resolving packages... success Already up-to-date. ✨ Done in 0.29s. Sunlight:js-dos stefan$ yarn run gulp yarn run v1.22.19 $ /Users/Shared/Developer/js-dos/node_modules/.bin/gulp [21:45:17] Requiring external module ts-node/register [21:45:19] Using gulpfile /Users/Shared/Developer/js-dos/gulpfile.ts [21:45:19] Starting 'default'... [21:45:19] Starting 'clean'... [21:45:19] Starting 'clean'... [21:45:19] Finished 'clean' after 14 ms [21:45:19] Starting 'types'... execute ./node_modules/.bin/tsc --emitDeclarationOnly --declaration src/player.ts --outDir dist/types [21:45:19] Finished 'clean' after 23 ms [21:45:19] Starting 'playerJs'... [21:45:19] Starting 'playerCss'... execute yarn run tailwindcss -i src/player.css -o build/player.css [21:45:21] 'playerJs' errored after 2.34 s [21:45:21] Error: Parsing file /Users/Shared/Developer/js-dos/src/components/sidebar/token/token.ts: Unexpected token (49:23) at Deps.parseDeps (/Users/Shared/Developer/js-dos/node_modules/module-deps/index.js:519:15) at getDeps (/Users/Shared/Developer/js-dos/node_modules/module-deps/index.js:447:44) at /Users/Shared/Developer/js-dos/node_modules/module-deps/index.js:430:38 at ConcatStream. (/Users/Shared/Developer/js-dos/node_modules/concat-stream/index.js:37:43) at ConcatStream.emit (events.js:412:35) at ConcatStream.emit (domain.js:537:15) at finishMaybe (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_writable.js:630:14) at endWritable (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_writable.js:638:3) at ConcatStream.Writable.end (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_writable.js:594:22) at DuplexWrapper.onend (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_readable.js:577:10) at Object.onceWrapper (events.js:519:28) at DuplexWrapper.emit (events.js:412:35) at DuplexWrapper.emit (domain.js:537:15) at endReadableNT (/Users/Shared/Developer/js-dos/node_modules/readable-stream/lib/_stream_readable.js:1010:12) at processTicksAndRejections (internal/process/task_queues.js:82:21) [21:45:21] 'default' errored after 2.37 s error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

caiiiycuk commented 1 year ago

paste the output of yarn run tsc --noEmit

NaLiJa commented 1 year ago

Sunlight:js-dos stefan$ yarn run tsc --noEmit yarn run v1.22.19 $ /Users/Shared/Developer/js-dos/node_modules/.bin/tsc --noEmit ✨ Done in 1.95s.

Thanks!

caiiiycuk commented 1 year ago

What is contents of /Users/Shared/Developer/js-dos/src/components/sidebar/token/token.ts. it's a bit starnge cause PlayerJS step is just tsc compiling step, so yarn run tsc should not work then

NaLiJa commented 1 year ago

Looks pretty normal, I guess

Sunlight:js-dos stefan$ cat /Users/Shared/Developer/js-dos/src/components/sidebar/token/token.ts
import { useEffect, useState } from "preact/hooks";
import { tokeInfoGetEndpoint, startIpx, stopIpx } from "../../../backend/v7/v7-config";
import { html } from "../../../dom";
import { Icons } from "../../../icons";
import { Props } from "../../../player-app";
import { getObject, postObject } from "../../../xhr";
import { request } from "../../../request";

import { TokenSelect } from "./token-select";
import { TokenAddTime } from "./token-add-time";

const initialWaitCountDown = 30;
const NETWORK_DOSBOX_IPX = 0;

interface Token {
    region: string,
    ttlSec: number,
    ipxArn?: string | null,
    ipxAddress?: string | null,
}

interface IpxProps {
    arn: string | null,
    setArn: (ipxArn: string | null) => void,

    address: string | null,
    setAddress: (ipxAddress: string | null) => void,

    awaitingAddress: boolean,
    setAwaitingAddress: (waitingIpx: boolean) => void,

    awaitingConnection: boolean,
    setAwaitingConnection: (waitingIpx: boolean) => void,
}
export interface TokenProps extends Props {
    ipx: IpxProps,
    update: () => void,
}

export function TokenConfiguration(props: Props) {
    const [token, setToken] = useState<Token | null>(null);
    const [awaiting, setAwaiting] = useState<boolean>(true);
    const [endTime, setEndTime] = useState<number>(Date.now());

    const [ipxArn, setIpxArn] = useState<string | null>(null);
    const [ipxAddress, setIpxAddress] = useState<string | null>(null);
    const [awaitingIpxAddress, setAwaitingIpxAddress] = useState<boolean>(false);
    const [awaintngIpxConnection, setAwaitingIpxConnection] = useState<boolean>(false);

    const ipxProps: IpxProps = {
        arn: ipxArn,
        setArn: setIpxArn,

        address: ipxAddress,
        setAddress: setIpxAddress,

        awaitingAddress: awaitingIpxAddress,
        setAwaitingAddress: setAwaitingIpxAddress,

        awaitingConnection: awaintngIpxConnection,
        setAwaitingConnection: setAwaitingIpxConnection,
    };

    const tokenProps: TokenProps = {
        ...props,
        ipx: ipxProps,
        update,
    };

    useEffect(() => {
        update();
    }, [props.networkToken]);

    useEffect(() => {
        if (props.networkToken === null || endTime < Date.now()) {
            return;
        }

        const id = setInterval(() => {
            request(tokeInfoGetEndpoint + `?token=${props.networkToken}`).then((token: Token) => {
                token.ipxArn ||= null;
                token.ipxAddress ||= null;

                // desync
                if (ipxArn !== token.ipxArn) {
                    update();
                    return;
                }

                if (token.ipxAddress !== ipxAddress) {
                    setIpxAddress(token.ipxAddress);
                    setAwaitingIpxAddress(false);
                }
            });
        }, 5000);

        return () => {
            clearInterval(id);
        };
    }, [props.networkToken, endTime, ipxArn, ipxAddress]);

    async function update() {
        setIpxArn(null);
        setIpxAddress(null);
        setAwaitingIpxAddress(false);
        setAwaiting(true);

        if (props.networkToken === null) {
            setToken(null);
            setAwaiting(false);
            return;
        }

        request(tokeInfoGetEndpoint + `?token=${props.networkToken}`).then((token: Token) => {
            setToken(token);
            setAwaiting(false);
            setEndTime(Date.now() + token.ttlSec * 1000);

            if (token.ipxArn !== undefined) {
                setIpxArn(token.ipxArn);
            }

            if (token.ipxAddress !== undefined) {
                setIpxAddress(token.ipxAddress);
            } else if (token.ipxArn !== undefined) {
                setAwaitingIpxAddress(true);
            }
        }).catch((e: any) => {
            console.error("Can't get a token", props.networkToken, e);
            setToken(null);
            setAwaiting(false);
        });
    }

    if (awaiting) {
        return html`
            <div class="sidebar-header">Configuration</div>
            <div class="grid grid-cols-2 gap-4">
                <${Icons.Refresh} class="w-6 h-6 animate-reverse-spin" />
            </div>
    `;
    }

    if (token === null) {
        return html`
            <div class="sidebar-header">Configuration</div>
            <div class="grid grid-cols-2 gap-4">
                <${TokenSelect} ...${props} networkToken=${null} />
            </div>
        `;
    }

    const headerWithRefresh = html`
        <div class="sidebar-header flex flex-row justify-center items-center">
            Configuration
            <div onClick=${update} >
                <${Icons.Refresh} class="h-4 w-4 ml-2 cursor-pointer" />
            </div>
        </div>
    `;

    if (endTime < Date.now()) {
        return html`
            ${headerWithRefresh}
            <div class="grid grid-cols-2 gap-4">
                <${TokenSelect} ...${props} />
                <div class="font-bold">Region:</div>
                <div class="text-gray-400">${token.region}</div>
                <div class="font-bold">TTL:</div>
                <div class="text-red-400">0 Min</div>
                <${TokenAddTime} ...${tokenProps} />
            </div>
        `;
    }

    return html`
        ${headerWithRefresh}
        <div class="grid grid-cols-2 gap-4">
            <${TokenSelect} ...${props} />
            <div class="font-bold">Region:</div>
            <div class="text-gray-400">${token.region}</div>
            <${TokenTtlCountDown} endTime=${endTime} update=${update} />
            <${TokenAddTime} ...${tokenProps} />
            <${IPX} ...${tokenProps} />
        </div>
    `;
}

function TokenTtlCountDown(props: { endTime: number, update: () => void }) {
    const [ttlMs, setTtlMs] = useState<number>(props.endTime - Date.now());

    useEffect(() => {
        if (ttlMs <= 0) {
            return;
        }

        const id = setInterval(() => {
            const ttlMs = Math.max(0, props.endTime - Date.now());
            if (ttlMs === 0) {
                props.update();
                clearInterval(id);
            }

            setTtlMs(ttlMs);
        }, 10000);
        return () => clearInterval(id);
    }, [props.endTime]);

    return html`
        <div class="font-bold">TTL:</div>
        <div class="${ttlMs < 300 * 1000 ? " text-red-400" : "text-gray-400"} cursor-pointer underline"
            onClick=${props.update}>
            ${humanizeTime(ttlMs / 1000)}
        </div>
    `;
}

function IPX(props: TokenProps) {
    const [awaiting, setAwaiting] = useState<boolean>(false);
    const [error, setError] = useState<string | null>(null);

    function start() {
        setAwaiting(true);
        getObject(startIpx + `?token=${props.networkToken}`)
            .then((response) => {
                setAwaiting(false);
                props.ipx.setArn(response.arn);
                props.ipx.setAwaitingAddress(true);
            })
            .catch((e) => {
                console.error("Can't start ipx", e);
                setError(e.errorCode ?? e.message);
                setAwaiting(false);
            });
    }

    function toggleConnected() {
        const newConnected = !props.ipxConnected;
        const address = props.ipx.address;
        const port = 1901;

        if (!address) {
            return;
        }

        props.player().ciPromise?.then((ci) => {
            if (newConnected) {
                props.ipx.setAwaitingConnection(true);

                if (location.protocol === "http:" &&
                    props.options().hardware === undefined &&
                    address.endsWith(".jj.dos.zone")) {
                    // dns optimization trick
                    return ci.networkConnect(NETWORK_DOSBOX_IPX,
                        "ws://" + address.substring(0, address.length - ".jj.dos.zone".length).replace(/_/g, "."),
                        port);
                } else {
                    return ci.networkConnect(NETWORK_DOSBOX_IPX, address, port);
                }
            }

            return ci.networkDisconnect(NETWORK_DOSBOX_IPX);
        })
            .then(() => {
                props.ipx.setAwaitingConnection(false);
                props.setIpxConnected(newConnected);
                if (newConnected) {
                    props.player().layers.notyf.success("Connected");
                    props.closeSideBar();
                }
            })
            .catch((e) => {
                props.ipx.setAwaitingConnection(false);
                console.error(e);
                setError(e.message);
            });
    }

    function stop() {
        setAwaiting(true);
        postObject(stopIpx + `?token=${props.networkToken}&arn=${props.ipx.arn}`)
            .then(() => {
                setAwaiting(false);
                props.ipx.setAddress(null);
                props.ipx.setAwaitingAddress(false);
            })
            .catch((e) => {
                console.error("Can't stop ipx", e);
                setError(e.errorCode ?? e.message);
                setAwaiting(false);
            });
    }

    if (error !== null) {
        return html`
            <div class="text-red-400 col-span-2">${error}</div>
        `;
    }

    if (awaiting) {
        return html`
            <${Icons.Refresh} class="w-6 h-6 col-span-2 animate-reverse-spin" />
        `;
    }

    if (props.ipx.address !== null) {
        const connectText =
            props.ipxConnected ? "Disconnect" :
                (props.ipx.awaitingConnection ? "Connecting..." : "Connect");

        const onConnectClick = () => {
            if (props.ipx.awaitingConnection) {
                return;
            }

            toggleConnected();
        };

        return html`
            <div class="font-bold">IPX:</div>
            <div class="font-bold text-gray-400 text-xs break-all -mx-6 text-center">${props.ipx.address}</div>
            <div class=""></div>
            <div class="${props.ipxConnected ? " bg-red-200" : "bg-green-200"}
                cursor-pointer rounded uppercase text-center px-2 py-1"
                onClick=${onConnectClick}>${connectText}</div>
            <div class="${props.ipxConnected ? "hidden" : ""}"></div>
            <div class="${props.ipxConnected ? "hidden" : "none"}
                bg-gray-200 cursor-pointer rounded uppercase text-center px-4 py-1"
                onClick=${stop}>Stop</div>
        `;
    }

    if (props.ipx.awaitingAddress) {
        return html`
            <div class="font-bold">IPX:</div>
            <${TaskWaitCountDown} />
            <div class=""></div>
            <div class="bg-gray-200 cursor-pointer rounded uppercase text-center px-4 py-1" onClick=${stop}>Stop</div>
        `;
    }

    return html`
        <div class="font-bold">IPX:</div>
        <div class="bg-green-200 cursor-pointer rounded uppercase text-center px-4 py-1" onClick=${start}>Start</div>
    `;
}

function TaskWaitCountDown() {
    const [countDown, setCountDown] = useState<number>(initialWaitCountDown);

    useEffect(() => {
        if (countDown === 0) {
            return;
        }

        const id = setTimeout(() => {
            setCountDown(countDown - 1);
        }, 1000);
        return () => clearTimeout(id);
    }, [countDown]);

    return html`
        <div class="text-gray-400 flex flex-row">
            <${Icons.Refresh} class="w-6 h-6 animate-reverse-spin mr-2" />
            ${countDown > 0 ? countDown + " sec" : ""}
        </div>
    `;
}

function humanizeTime(timeSec: number) {
    if (timeSec > 24 * 60 * 60) {
        const days = Math.round(timeSec / 24 / 60 / 60 * 10) / 10;
        return days + (days === 1 ? " Day" : " Days");
    }

    if (timeSec > 60 * 60) {
        const hours = Math.round(timeSec / 60 / 60 * 10) / 10;
        return hours + (hours === 1 ? " Hour" : " Hrs");
    }

    const minutes = Math.round(timeSec / 60 * 10) / 10;
    return minutes + " Min";
}
NaLiJa commented 1 year ago

Ok, after spending much more time with trying to solve this, I gave up and set up a Linux VM fpr building and there the build runs without problems. Seems, either something is borked up on my machine or it is one of the gazillion mac inconsistencies.

Both js-dos and emulator-ui build, now I need to find out how to make js-dos use the local emulator-ui build instead of the repo module...