diff --git a/doric-cli/src/actions.ts b/doric-cli/src/actions.ts index f8a0be60..3f17af66 100644 --- a/doric-cli/src/actions.ts +++ b/doric-cli/src/actions.ts @@ -17,16 +17,21 @@ export async function clean() { await Shell.exec("rm", ["-rf", "bundle"]); } -export async function doMerge(jsFile: string) { - const mappingFile = `${jsFile}.map`; +async function doMerge(jsFile: string) { + const mapFile = `${jsFile}.map`; console.log(`Bundle -> ${jsFile.green}`); - if (!fs.existsSync(mappingFile)) { + if (!fs.existsSync(mapFile)) { return; } - console.log(` -> ${mappingFile.green}`); - const mergedMap = createMergedSourceMapFromFiles([ - mappingFile.replace(/bundle\//, 'build/'), - mappingFile, - ], true); - await fs.promises.writeFile(mappingFile, mergedMap); + console.log(` -> ${mapFile.green}`); + await mergeMap(mapFile); } + + +export async function mergeMap(mapFile: string) { + const mergedMap = createMergedSourceMapFromFiles([ + mapFile.replace(/bundle\//, 'build/'), + mapFile, + ], true); + await fs.promises.writeFile(mapFile, mergedMap); +} \ No newline at end of file diff --git a/doric-cli/src/dev.ts b/doric-cli/src/dev.ts new file mode 100644 index 00000000..2bf1e066 --- /dev/null +++ b/doric-cli/src/dev.ts @@ -0,0 +1,99 @@ +import { exec } from "child_process" +import chokidar from "chokidar"; +import { createServer } from "./server" +import { delay } from "./util"; +import fs from "fs"; +import { mergeMap } from "./actions"; +import os from "os"; +import qrcode from "qrcode-terminal"; +import keypress from "keypress"; + +function getIPAdress() { + const ret: string[] = []; + const interfaces = os.networkInterfaces(); + Object.entries(interfaces).map(e => e[1]) + .forEach(e => { + if (!!!e) { + return; + } + e.forEach(e => { + if ( + e.family === "IPv4" && + e.address !== "127.0.0.1" && + !e.internal + ) { + ret.push(e.address); + } + }) + }); + return ret; +} + +export default async function dev() { + const server = await createServer() + const tscProcess = exec("node node_modules/.bin/tsc -w -p ."); + const rollupProcess = exec("node node_modules/.bin/rollup -c -w"); + console.warn("Waiting ..."); + const ips = getIPAdress(); + ips.forEach((e) => { + console.log(`IP:${e}`); + qrcode.generate(e, { small: true }); + }); + + keypress(process.stdin); + process.stdin.on("keypress", function (ch, key) { + if (key && key.ctrl && key.name == "r") { + ips.forEach((e) => { + console.log(`IP:${e}`); + qrcode.generate(e, { small: true }); + }); + } + if (key && key.ctrl && key.name == "c") { + process.stdin.pause(); + tscProcess.kill("SIGABRT"); + rollupProcess.kill("SIGABRT"); + process.exit(0); + } + }); + process.stdin.setRawMode(true); + process.stdin.resume(); + await delay(3000); + console.warn("Start watching"); + server.listen(7777); + chokidar + .watch(process.cwd() + "/bundle", { + ignored: /.*?\.map/, + alwaysStat: true, + }) + .on("change", (jsFile) => { + console.log("*******", jsFile.replace(process.cwd(), "") + .replace("/bundle/src/", "") + .replace(".js", "") + .green, "*******") + if ((server as any).debugging) { + console.log("debugging, hot reload by pass"); + return; + } + fs.readFile(jsFile, "utf-8", (error, data) => { + try { + const sourceMap = mergeMap(`${jsFile}.map`); + server.connections.forEach((e: any) => { + e.sendText( + JSON.stringify({ + cmd: "RELOAD", + script: data, + source: (jsFile.match(/[^/\\]*$/) || [""])[0], + sourceMap, + }) + ); + }); + } catch (e) { + console.error(e); + } + }); + }); + + +} + + diff --git a/doric-cli/src/index.ts b/doric-cli/src/index.ts index 22b40e00..4b236dbc 100644 --- a/doric-cli/src/index.ts +++ b/doric-cli/src/index.ts @@ -3,7 +3,7 @@ import commander from "commander" import { build, clean } from "./actions"; import create from "./create" - +import dev from "./dev" commander .command('create ') .action(async function (name, cmd) { @@ -16,7 +16,8 @@ commander }) commander .command('dev') - .action(function () { + .action(async function () { + await dev() }) commander .command('build') diff --git a/doric-cli/src/modules.d.ts b/doric-cli/src/modules.d.ts index 2242d7c2..9f4a1e04 100644 --- a/doric-cli/src/modules.d.ts +++ b/doric-cli/src/modules.d.ts @@ -1 +1,4 @@ -declare module 'source-map-merger' \ No newline at end of file +declare module 'source-map-merger' +declare module 'nodejs-websocket' +declare module 'qrcode-terminal' +declare module 'keypress' \ No newline at end of file diff --git a/doric-cli/src/server.ts b/doric-cli/src/server.ts new file mode 100644 index 00000000..013449dd --- /dev/null +++ b/doric-cli/src/server.ts @@ -0,0 +1,77 @@ +import fs from "fs"; +import { exec, spawn } from "child_process"; +import ws from "nodejs-websocket"; +import "colors"; + +export async function createServer() { + console.log("Create Server") + let contextId: string = "0" + let clientConnection: any = null + let debuggerConnection: any = null + const server = (ws as any).createServer((connection: any) => { + console.log('Connected', connection.headers.host) + if (connection.headers.host.startsWith("localhost")) { + console.log(`Debugger ${connection.key} attached to dev kit`.green) + debuggerConnection = connection + clientConnection.sendText(JSON.stringify({ + cmd: 'SWITCH_TO_DEBUG', + contextId: contextId + }), () => { }) + } else { + console.log(`Client ${connection.key} attached to dev kit`.green) + } + + connection.on('text', function (result: string) { + let resultObject = JSON.parse(result) + switch (resultObject.cmd) { + case 'DEBUG': + clientConnection = connection; + (server as any).debugging = true; + console.log("Enter debugging"); + contextId = resultObject.data.contextId; + let projectHome = '.'; + + fs.writeFileSync(projectHome + '/build/context', contextId, 'utf8'); + + let source = resultObject.data.source; + console.log(connection.key + " request debug, project home: " + projectHome); + spawn('code', [projectHome, projectHome + "/src/" + source]); + setTimeout(() => { + exec('osascript -e \'tell application "System Events"\ntell application "Visual Studio Code" to activate\nkey code 96\nend tell\'', (err, stdout, stderr) => { + if (err) { + console.log(`stdout: ${err}`) + } + }) + }, 1500); + break; + case 'EXCEPTION': + console.log(resultObject.data.source.red); + console.log(resultObject.data.exception.red); + break; + case 'LOG': + if (resultObject.data.type == 'DEFAULT') { + console.log((resultObject.data.message as string).green); + } else if (resultObject.data.type == 'ERROR') { + console.log((resultObject.data.message as string).red); + } else if (resultObject.data.type == 'WARN') { + console.log((resultObject.data.message as string).yellow); + } + break + } + }) + connection.on('connect', function (code: number) { + console.log('connect', code) + }) + connection.on('close', function (code: number) { + console.log('close: code = ' + code, connection.key) + console.log("quit debugging"); + (server as any).debugging = false + }) + connection.on('error', function (code: number) { + console.log('error', code) + }) + }) + return server +} + + diff --git a/doric-cli/src/util.ts b/doric-cli/src/util.ts index 49474964..e59e25c0 100644 --- a/doric-cli/src/util.ts +++ b/doric-cli/src/util.ts @@ -1,5 +1,13 @@ import globLib, { IOptions } from "glob"; +export async function delay(timeout: number) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(''); + }, timeout); + }); +} + export function getAssetsDir() { return `${__dirname}/../assets`; }