diff --git a/.gitignore b/.gitignore index 6704566..7ad93d8 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,4 @@ dist # TernJS port file .tern-port +test.js \ No newline at end of file diff --git a/README.md b/README.md index 89f4b46..919ed92 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,10 @@ This command will work in [GitHub codespaces](https://github.com/features/codesp jacdac devtools ``` +#### `jacscript devtools --jacscript ` + +Starts the devtools web site and also watches/uploads the source of a given jacscript to the development web site. The dev web site will automatically compile and potentially deploy the jacscript program to a connected device. + ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a diff --git a/package.json b/package.json index 061f464..6a4947b 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "dependencies": { "commander": "^9.0.0", "faye-websocket": "^0.11.4", - "jacdac-ts": "^1.27.0" + "jacdac-ts": "^1.27.0", + "node-watch": "^0.7.3" }, "devDependencies": { "@semantic-release/exec": "^6.0.3", @@ -59,8 +60,8 @@ }, "optionalDependencies": { "rpio": "^2.4.2", - "spi-device": "^3.1.2", "serialport": "^9.2.8", + "spi-device": "^3.1.2", "usb": "^2.1.2" }, "files": [ diff --git a/src/devtools.ts b/src/devtools.ts index bb5b9e7..841764f 100644 --- a/src/devtools.ts +++ b/src/devtools.ts @@ -12,6 +12,8 @@ import { Flags, JDFrameBuffer, FRAME_PROCESS, + JSONTryParse, + prettySize, } from "jacdac-ts" import { enableLogging } from "./jdlogging" import { createTransports, TransportsOptions } from "./transports" @@ -29,10 +31,14 @@ const log = console.log const debug = console.debug const error = console.error -function fetchProxy(): Promise { - const url = "https://microsoft.github.io/jacdac-docs/devtools/proxy" +function fetchProxy(localhost: boolean): Promise { + const protocol = localhost ? http : https + const url = localhost + ? "http://localhost:8000/devtools/proxy.html" + : "https://microsoft.github.io/jacdac-docs/devtools/proxy" + console.debug(`fetch devtools proxy at ${url}`) return new Promise((resolve, reject) => { - https + protocol .get(url, res => { if (res.statusCode != 200) reject( @@ -41,7 +47,15 @@ function fetchProxy(): Promise { res.setEncoding("utf8") let body = "" res.on("data", data => (body += data)) - res.on("end", () => resolve(body)) + res.on("end", () => { + if (localhost) { + body = body.replace( + /https:\/\/microsoft.github.io\/jacdac-docs\/dashboard/g, + "http://localhost:8000/dashboard" + ) + } + resolve(body) + }) res.on("error", reject) }) .on("error", reject) @@ -56,10 +70,18 @@ export async function devToolsCommand( trace?: string diagnostics?: boolean localhost?: boolean + jacscript?: string } & TransportsOptions ) { - const { packets, internet, trace, logging, diagnostics, localhost } = - options || {} + const { + packets, + internet, + trace, + logging, + diagnostics, + localhost, + jacscript: jacscriptFile, + } = options || {} const port = 8081 const tcpPort = 8082 const listenHost = internet ? undefined : "127.0.0.1" @@ -73,16 +95,27 @@ export async function devToolsCommand( log(` raw socket: tcp://localhost:${tcpPort}`) // download proxy sources - let proxyHtml = await fetchProxy() - if (localhost) - proxyHtml = proxyHtml.replace( - /https:\/\/microsoft.github.io\/jacdac-docs\/dashboard/g, - "http://localhost:8000/dashboard" - ) + const proxyHtml = await fetchProxy(localhost) // start http server const clients: WebSocket[] = [] + // upload jacscript file is needed + const sendJacscript = jacscriptFile + ? () => { + const source = fs.readFileSync(jacscriptFile, { + encoding: "utf-8", + }) + console.debug(`refresh jacscript (${prettySize(source.length)})`) + const msg = JSON.stringify({ + type: "source", + channel: "jacscript", + source, + }) + clients.forEach(c => c.send(msg)) + } + : undefined + const server = http.createServer(function (req, res) { const parsedUrl = url.parse(req.url) const pathname = parsedUrl.pathname @@ -118,6 +151,11 @@ export async function devToolsCommand( }) bridge.on(FRAME_PROCESS, forwardFrame) bus.addBridge(bridge) + const processMessage = (message: string, sender: string) => { + const msg = JSONTryParse(message) + if (!msg) return + console.debug(msg) + } const processPacket = (message: Buffer | Uint8Array, sender: string) => { const data = new Uint8Array(message) bridge.receiveFrameOrPacket(data, sender) @@ -140,10 +178,12 @@ export async function devToolsCommand( log(`client: connected (${sender}, ${clients.length} clients)`) client.on("message", (event: any) => { const { data } = event - processPacket(data, sender) + if (typeof data === "string") processMessage(data, sender) + else processPacket(data, sender) }) client.on("close", () => removeClient(client)) client.on("error", (ev: Error) => error(ev)) + if (sendJacscript) sendJacscript() } }) @@ -207,4 +247,11 @@ export async function devToolsCommand( bus.connect(true) server.listen(port, listenHost) tcpServer.listen(tcpPort, listenHost) + + if (sendJacscript) { + console.debug(`watch ${jacscriptFile}`) + fs.watch(jacscriptFile, async (eventType, filename) => { + sendJacscript() + }) + } } diff --git a/src/index.ts b/src/index.ts index 4ef0391..4f9ad16 100644 --- a/src/index.ts +++ b/src/index.ts @@ -58,6 +58,10 @@ async function mainCli() { "--localhost", "use localhost:8000 instead of the internet dashboard" ) + .option( + "-j, --jacscript ", + "upload and watch source of local jacscript file" + ) .action(devToolsCommand) await program.parseAsync(process.argv)