@githubnext/vitale
Advanced tools
Comparing version
774
dist/cli.js
@@ -7,13 +7,69 @@ #!/usr/bin/env node | ||
// package.json | ||
var version = "0.0.14"; | ||
var version = "0.0.15"; | ||
// src/server.ts | ||
import { createBirpc } from "birpc"; | ||
import corsMiddleware from "cors"; | ||
import JSON5 from "json5"; | ||
import * as Path from "node:path"; | ||
import * as Path2 from "node:path"; | ||
import { createServer as createViteServer, send } from "vite"; | ||
import { ESModulesRunner, ViteRuntime } from "vite/runtime"; | ||
import { WebSocketServer } from "ws"; | ||
// src/cells.ts | ||
var cellIdRegex = /^([^?]+\.vnb)-cellId=([a-zA-z0-9_-]{21})\.([a-z]+)$/; | ||
function extOfLanguage(language) { | ||
switch (language) { | ||
case "typescriptreact": | ||
return "tsx"; | ||
case "typescript": | ||
return "ts"; | ||
case "javascriptreact": | ||
return "jsx"; | ||
case "javascript": | ||
return "js"; | ||
default: | ||
throw new Error(`unknown language "${language}"`); | ||
} | ||
} | ||
var Cells = class { | ||
cellsByPath = /* @__PURE__ */ new Map(); | ||
get(id) { | ||
const match = cellIdRegex.exec(id); | ||
if (match) { | ||
const [_, path2, cellId, ext] = match; | ||
const cells = this.cellsByPath.get(path2); | ||
if (cells) { | ||
const cell = cells.get(cellId); | ||
if (cell && extOfLanguage(cell.language) === ext) { | ||
return cell; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
set(id, cell) { | ||
const match = cellIdRegex.exec(id); | ||
if (match) { | ||
const [_, path2, cellId] = match; | ||
let cells = this.cellsByPath.get(path2); | ||
if (!cells) { | ||
cells = /* @__PURE__ */ new Map(); | ||
this.cellsByPath.set(path2, cells); | ||
} | ||
cells.set(cellId, cell); | ||
} | ||
} | ||
delete(id) { | ||
const match = cellIdRegex.exec(id); | ||
if (match) { | ||
const [_, path2, cellId] = match; | ||
const cells = this.cellsByPath.get(path2); | ||
if (cells) { | ||
cells.delete(cellId); | ||
} | ||
} | ||
} | ||
forPath(path2) { | ||
return this.cellsByPath.get(path2) ?? /* @__PURE__ */ new Map(); | ||
} | ||
}; | ||
// src/rewrite.ts | ||
@@ -224,2 +280,326 @@ import _babelGenerator from "@babel/generator"; | ||
// src/rpc.ts | ||
import { createBirpc } from "birpc"; | ||
import JSON5 from "json5"; | ||
// src/domain.ts | ||
import * as Domain from "node:domain"; | ||
var originalStdoutWrite = process.stdout.write.bind(process.stdout); | ||
var originalStderrWrite = process.stderr.write.bind(process.stderr); | ||
var processWithDomain = process; | ||
process.stdout.write = (chunk, ...args) => { | ||
if (processWithDomain.domain && processWithDomain.domain.stdoutWrite) { | ||
processWithDomain.domain.stdoutWrite(Buffer.from(chunk)); | ||
} | ||
return originalStdoutWrite(chunk, ...args); | ||
}; | ||
process.stderr.write = (chunk, ...args) => { | ||
if (processWithDomain.domain && processWithDomain.domain.stderrWrite) { | ||
processWithDomain.domain.stderrWrite(Buffer.from(chunk)); | ||
} | ||
return originalStderrWrite(chunk, ...args); | ||
}; | ||
function createDomain(stdoutWrite, stderrWrite) { | ||
const domain = Domain.create(); | ||
domain.stdoutWrite = stdoutWrite; | ||
domain.stderrWrite = stderrWrite; | ||
return domain; | ||
} | ||
// src/executeCell.ts | ||
function isSVGElementLike(obj) { | ||
return obj !== null && typeof obj === "object" && "outerHTML" in obj && typeof obj.outerHTML === "string" && obj.outerHTML.startsWith("<svg"); | ||
} | ||
function isHTMLElementLike(obj) { | ||
return obj !== null && typeof obj === "object" && "outerHTML" in obj && typeof obj.outerHTML === "string"; | ||
} | ||
function rewriteStack(stack) { | ||
if (!stack) { | ||
return stack; | ||
} | ||
const i = stack.indexOf("\n at ESModulesRunner.runViteModule"); | ||
if (i !== -1) { | ||
return stack.substring(0, i); | ||
} else { | ||
return stack; | ||
} | ||
} | ||
function mimeTaggedResultOf(result) { | ||
if (result === void 0) { | ||
return void 0; | ||
} else if (result instanceof Error) { | ||
const obj = { | ||
name: result.name, | ||
message: result.message, | ||
stack: rewriteStack(result.stack) | ||
}; | ||
return { | ||
data: JSON.stringify(obj, void 0, " "), | ||
mime: "application/vnd.code.notebook.error" | ||
}; | ||
} else if (isSVGElementLike(result)) { | ||
return { mime: "image/svg+xml", data: result.outerHTML }; | ||
} else if (isHTMLElementLike(result)) { | ||
return { mime: "text/html", data: result.outerHTML }; | ||
} else if (typeof result === "object" && "data" in result && typeof result.data === "string" && "mime" in result && typeof result.mime === "string") { | ||
return result; | ||
} else if (typeof result === "object") { | ||
return { mime: "application/json", data: JSON.stringify(result) }; | ||
} else { | ||
return { mime: "text/x-javascript", data: JSON.stringify(result) }; | ||
} | ||
} | ||
function makeCellOutput(result) { | ||
const mimeTaggedResult = mimeTaggedResultOf(result); | ||
const items = []; | ||
if (mimeTaggedResult !== void 0) { | ||
items.push({ | ||
data: [...Buffer.from(mimeTaggedResult.data, "utf8").values()], | ||
mime: mimeTaggedResult.mime | ||
}); | ||
} | ||
const cellOutput = { items }; | ||
return cellOutput; | ||
} | ||
async function endCellExecutionWithOutput(rpc, path2, cellId, result) { | ||
const cellOutput = makeCellOutput(result); | ||
await rpc.endCellExecution(path2, cellId, cellOutput); | ||
return true; | ||
} | ||
function isIterator(obj) { | ||
return obj && typeof obj.next === "function" && typeof obj.return === "function" && typeof obj.throw === "function"; | ||
} | ||
async function executeCell(rpc, cells, runtime, id, path2, cellId, force) { | ||
const startOK = await rpc.startCellExecution(path2, cellId, force); | ||
if (!startOK) { | ||
return false; | ||
} | ||
const cell = cells.get(id); | ||
if (!cell) { | ||
try { | ||
throw new Error(`cell not found: ${id}`); | ||
} catch (e) { | ||
return await endCellExecutionWithOutput(rpc, path2, cellId, e); | ||
} | ||
} | ||
if (!cell.sourceDescription) { | ||
const [_, path3] = cellIdRegex.exec(id); | ||
try { | ||
cell.sourceDescription = rewrite_default( | ||
cell.code, | ||
cell.language, | ||
id, | ||
cell.cellId, | ||
cells.forPath(path3) | ||
); | ||
} catch (e) { | ||
return await endCellExecutionWithOutput(rpc, path3, cellId, e); | ||
} | ||
} | ||
if (cell.sourceDescription.type === "client") { | ||
const result2 = { | ||
data: JSON.stringify({ | ||
// TODO(jaked) strip workspace root when executeCell is called | ||
id: id.substring(runtime.root.length + 1), | ||
origin: runtime.origin | ||
}), | ||
mime: "application/x-vitale" | ||
}; | ||
return await endCellExecutionWithOutput(rpc, path2, cellId, result2); | ||
} | ||
const domain = createDomain( | ||
(chunk) => { | ||
rpc.outputStdout(path2, cellId, chunk.toString("utf8")); | ||
}, | ||
(chunk) => { | ||
rpc.outputStderr(path2, cellId, chunk.toString("utf8")); | ||
} | ||
); | ||
let result; | ||
result = await domain.run(async () => await runtime.executeUrl(id)).then((mod) => mod.default).catch((e) => e); | ||
if (result instanceof Promise) { | ||
result = await domain.run(async () => await result).catch((e) => e); | ||
return await endCellExecutionWithOutput(rpc, path2, cellId, result); | ||
} else if (isIterator(result)) { | ||
while (true) { | ||
const item = await domain.run(async () => await result.next()).catch((e) => e); | ||
if (item instanceof Error) { | ||
return await endCellExecutionWithOutput(rpc, path2, cellId, item); | ||
} | ||
if (item.value !== void 0) { | ||
await rpc.updateCellOutput(path2, cellId, makeCellOutput(item.value)); | ||
} | ||
if (item.done) { | ||
rpc.endCellExecution(path2, cellId); | ||
return true; | ||
} | ||
} | ||
} else { | ||
return await endCellExecutionWithOutput(rpc, path2, cellId, result); | ||
} | ||
} | ||
// src/rpc.ts | ||
function extOfLanguage2(language) { | ||
switch (language) { | ||
case "typescriptreact": | ||
return "tsx"; | ||
case "typescript": | ||
return "ts"; | ||
case "javascriptreact": | ||
return "jsx"; | ||
case "javascript": | ||
return "js"; | ||
default: | ||
throw new Error(`unknown language "${language}"`); | ||
} | ||
} | ||
var Rpc = class { | ||
clients = /* @__PURE__ */ new Map(); | ||
cells; | ||
runtime; | ||
constructor(cells, runtime) { | ||
this.cells = cells; | ||
this.runtime = runtime; | ||
} | ||
startCellExecution(path2, cellId, force) { | ||
return Promise.all( | ||
Array.from(this.clients.values()).map( | ||
(client) => client.startCellExecution(path2, cellId, force) | ||
) | ||
).then((oks) => oks.every((ok) => ok)); | ||
} | ||
outputStdout(path2, cellId, output) { | ||
for (const client of this.clients.values()) { | ||
client.outputStdout(path2, cellId, output); | ||
} | ||
} | ||
outputStderr(path2, cellId, output) { | ||
for (const client of this.clients.values()) { | ||
client.outputStderr(path2, cellId, output); | ||
} | ||
} | ||
updateCellOutput(path2, cellId, cellOutput) { | ||
return Promise.all( | ||
Array.from(this.clients.values()).map( | ||
(client) => client.updateCellOutput(path2, cellId, cellOutput) | ||
) | ||
); | ||
} | ||
endCellExecution(path2, cellId, cellOutput) { | ||
return Promise.all( | ||
Array.from(this.clients.values()).map( | ||
(client) => client.endCellExecution(path2, cellId, cellOutput) | ||
) | ||
); | ||
} | ||
markCellsDirty(cells) { | ||
if (cells.length === 0) { | ||
return; | ||
} | ||
for (const client of this.clients.values()) { | ||
client.markCellsDirty(cells); | ||
} | ||
} | ||
setupClient(ws) { | ||
const self = this; | ||
const rpc = createBirpc( | ||
{ | ||
ping: async () => { | ||
console.log("ping"); | ||
return "pong"; | ||
}, | ||
async executeCells(cells, force, executeDirtyCells) { | ||
try { | ||
return self.executeCellsRPC(cells, force, executeDirtyCells); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
}, | ||
async removeCells(cells) { | ||
try { | ||
return self.removeCellsRPC(cells); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
}, | ||
{ | ||
post: (msg) => ws.send(msg), | ||
on: (fn) => ws.on("message", fn), | ||
serialize: (v) => JSON5.stringify(v), | ||
deserialize: (v) => JSON5.parse(v) | ||
} | ||
); | ||
this.clients.set(ws, rpc); | ||
ws.on("close", () => { | ||
this.clients.delete(ws); | ||
}); | ||
} | ||
async executeCellsRPC(cells, force, executeDirtyCells) { | ||
let dirtyCells = []; | ||
for (const { path: path2, cellId, language, code } of cells) { | ||
const ext = extOfLanguage2(language); | ||
const id = `${path2}-cellId=${cellId}.${ext}`; | ||
if (code) { | ||
this.cells.set(id, { cellId, code, language }); | ||
} | ||
this.runtime.invalidateServerModule(id); | ||
this.runtime.handleHMRUpdate(id); | ||
this.runtime.invalidateRuntimeModule(id, dirtyCells); | ||
} | ||
dirtyCells = dirtyCells.filter( | ||
(dirtyCell) => !cells.some( | ||
(cell) => cell.path === dirtyCell.path && cell.cellId === dirtyCell.cellId | ||
) | ||
); | ||
const cellsToExecute = [ | ||
...cells.map(({ path: path2, cellId, language }) => ({ | ||
path: path2, | ||
cellId, | ||
ext: extOfLanguage2(language), | ||
force | ||
})), | ||
...executeDirtyCells ? dirtyCells.map((cell) => ({ ...cell, force: false })) : [] | ||
]; | ||
const executed = await Promise.all( | ||
cellsToExecute.map( | ||
({ path: path2, cellId, ext, force: force2 }) => executeCell( | ||
this, | ||
this.cells, | ||
this.runtime, | ||
`${path2}-cellId=${cellId}.${ext}`, | ||
path2, | ||
cellId, | ||
force2 | ||
) | ||
) | ||
); | ||
const cellsToMarkDirty = cellsToExecute.filter( | ||
({ force: force2 }, i) => !force2 && !executed[i] | ||
); | ||
this.markCellsDirty(cellsToMarkDirty); | ||
} | ||
removeCellsRPC(cells) { | ||
let dirtyCells = []; | ||
for (const { path: path2, cellId, language } of cells) { | ||
const ext = extOfLanguage2(language); | ||
const id = `${path2}-cellId=${cellId}.${ext}`; | ||
this.cells.delete(id); | ||
this.runtime.invalidateServerModule(id); | ||
this.runtime.invalidateRuntimeModule(id, dirtyCells); | ||
} | ||
dirtyCells = dirtyCells.filter( | ||
(dirtyCell) => !cells.some( | ||
(cell) => cell.path === dirtyCell.path && cell.cellId === dirtyCell.cellId | ||
) | ||
); | ||
this.markCellsDirty(dirtyCells); | ||
} | ||
}; | ||
// src/runtime.ts | ||
import { ESModulesRunner, ViteRuntime } from "vite/runtime"; | ||
import * as Path from "node:path"; | ||
// src/hmr.ts | ||
@@ -388,19 +768,54 @@ import path from "node:path"; | ||
// src/server.ts | ||
import * as Domain from "node:domain"; | ||
var originalStdoutWrite = process.stdout.write.bind(process.stdout); | ||
var originalStderrWrite = process.stderr.write.bind(process.stderr); | ||
var processWithDomain = process; | ||
process.stdout.write = (chunk, ...args) => { | ||
if (processWithDomain.domain && processWithDomain.domain.stdoutWrite) { | ||
processWithDomain.domain.stdoutWrite(Buffer.from(chunk)); | ||
// src/runtime.ts | ||
var Runtime = class { | ||
viteServer; | ||
viteRuntime; | ||
constructor(viteServer) { | ||
this.viteServer = viteServer; | ||
this.viteRuntime = new ViteRuntime( | ||
{ | ||
root: viteServer.config.root, | ||
fetchModule: viteServer.ssrFetchModule | ||
}, | ||
new ESModulesRunner() | ||
); | ||
} | ||
return originalStdoutWrite(chunk, ...args); | ||
}; | ||
process.stderr.write = (chunk, ...args) => { | ||
if (processWithDomain.domain && processWithDomain.domain.stderrWrite) { | ||
processWithDomain.domain.stderrWrite(Buffer.from(chunk)); | ||
invalidateServerModule(id) { | ||
const mod = this.viteServer.moduleGraph.getModuleById(id); | ||
if (mod) { | ||
this.viteServer.moduleGraph.invalidateModule(mod); | ||
} | ||
} | ||
return originalStderrWrite(chunk, ...args); | ||
handleHMRUpdate(id) { | ||
handleHMRUpdate(id, this.viteServer); | ||
} | ||
invalidateRuntimeModule(id, dirtyCells) { | ||
const mod = this.viteRuntime.moduleCache.get(id); | ||
this.viteRuntime.moduleCache.delete(id); | ||
const match = cellIdRegex.exec(id); | ||
if (match) { | ||
const [_, path2, cellId, ext] = match; | ||
if (!dirtyCells.some((cell) => cell.path === path2 && cell.cellId === cellId)) { | ||
dirtyCells.push({ path: path2, cellId, ext }); | ||
} | ||
} | ||
for (const dep of mod.importers ?? []) { | ||
this.invalidateRuntimeModule( | ||
Path.join(this.viteServer.config.root, dep), | ||
dirtyCells | ||
); | ||
} | ||
} | ||
executeUrl(id) { | ||
return this.viteRuntime.executeUrl(id); | ||
} | ||
get root() { | ||
return this.viteServer.config.root; | ||
} | ||
get origin() { | ||
return this.viteServer.config.server.origin; | ||
} | ||
}; | ||
// src/server.ts | ||
var trailingSeparatorRE = /[?&]$/; | ||
@@ -415,23 +830,2 @@ var timestampRE = /\bt=\d{13}&?\b/; | ||
} | ||
function isSVGElementLike(obj) { | ||
return obj !== null && typeof obj === "object" && "outerHTML" in obj && typeof obj.outerHTML === "string" && obj.outerHTML.startsWith("<svg"); | ||
} | ||
function isHTMLElementLike(obj) { | ||
return obj !== null && typeof obj === "object" && "outerHTML" in obj && typeof obj.outerHTML === "string"; | ||
} | ||
var cellIdRegex = /^([^?]+\.vnb)-cellId=([a-zA-z0-9_-]{21})\.([a-z]+)$/; | ||
function extOfLanguage(language) { | ||
switch (language) { | ||
case "typescriptreact": | ||
return "tsx"; | ||
case "typescript": | ||
return "ts"; | ||
case "javascriptreact": | ||
return "jsx"; | ||
case "javascript": | ||
return "js"; | ||
default: | ||
throw new Error(`unknown language "${language}"`); | ||
} | ||
} | ||
function makeHtmlSource(url) { | ||
@@ -467,52 +861,5 @@ const [_, _path, cellId] = cellIdRegex.exec(url); | ||
} | ||
function getCellById(cellsByPath, id) { | ||
const match = cellIdRegex.exec(id); | ||
if (match) { | ||
const [_, path2, cellId, ext] = match; | ||
const cells = cellsByPath.get(path2); | ||
if (cells) { | ||
const cell = cells.get(cellId); | ||
if (cell && extOfLanguage(cell.language) === ext) { | ||
return cell; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
function setCellById(cellsByPath, id, cell) { | ||
const match = cellIdRegex.exec(id); | ||
if (match) { | ||
const [_, path2, cellId] = match; | ||
let cells = cellsByPath.get(path2); | ||
if (!cells) { | ||
cells = /* @__PURE__ */ new Map(); | ||
cellsByPath.set(path2, cells); | ||
} | ||
cells.set(cellId, cell); | ||
} | ||
} | ||
function deleteCellById(cellsByPath, id) { | ||
const match = cellIdRegex.exec(id); | ||
if (match) { | ||
const [_, path2, cellId] = match; | ||
const cells = cellsByPath.get(path2); | ||
if (cells) { | ||
cells.delete(cellId); | ||
} | ||
} | ||
} | ||
function rewriteStack(stack) { | ||
if (!stack) { | ||
return stack; | ||
} | ||
const i = stack.indexOf("\n at ESModulesRunner.runViteModule"); | ||
if (i !== -1) { | ||
return stack.substring(0, i); | ||
} else { | ||
return stack; | ||
} | ||
} | ||
var VitaleDevServer = class _VitaleDevServer { | ||
static async construct(options) { | ||
const cellsByPath = /* @__PURE__ */ new Map(); | ||
const cells = new Cells(); | ||
let origin; | ||
@@ -537,8 +884,8 @@ const codespaceName = process.env.CODESPACE_NAME; | ||
resolveId(source) { | ||
const id = source.startsWith(viteServer.config.root) ? source : Path.join(viteServer.config.root, source); | ||
const cell = getCellById(cellsByPath, id); | ||
const id = source.startsWith(viteServer.config.root) ? source : Path2.join(viteServer.config.root, source); | ||
const cell = cells.get(id); | ||
return cell ? id : null; | ||
}, | ||
load(id) { | ||
const cell = getCellById(cellsByPath, id); | ||
const cell = cells.get(id); | ||
if (!cell) { | ||
@@ -554,3 +901,3 @@ return null; | ||
cell.cellId, | ||
cellsByPath.get(path2) | ||
cells.forPath(path2) | ||
); | ||
@@ -566,3 +913,3 @@ } | ||
const url = removeHtmlQuery(removeTimestampQuery(req.url)); | ||
if (getCellById(cellsByPath, Path.join(server.config.root, url))) { | ||
if (cells.get(Path2.join(server.config.root, url))) { | ||
if (htmlQuery) { | ||
@@ -595,18 +942,11 @@ const html = await server.transformIndexHtml( | ||
}); | ||
return new _VitaleDevServer(viteServer, cellsByPath); | ||
return new _VitaleDevServer(viteServer, cells); | ||
} | ||
viteServer; | ||
viteRuntime; | ||
clients = /* @__PURE__ */ new Map(); | ||
cellsByPath; | ||
constructor(viteServer, cellsByPath) { | ||
runtime; | ||
rpc; | ||
constructor(viteServer, cells) { | ||
this.viteServer = viteServer; | ||
this.cellsByPath = cellsByPath; | ||
this.viteRuntime = new ViteRuntime( | ||
{ | ||
root: viteServer.config.root, | ||
fetchModule: viteServer.ssrFetchModule | ||
}, | ||
new ESModulesRunner() | ||
); | ||
this.runtime = new Runtime(viteServer); | ||
this.rpc = new Rpc(cells, this.runtime); | ||
const wss = new WebSocketServer({ noServer: true }); | ||
@@ -621,3 +961,3 @@ viteServer.httpServer?.on("upgrade", (request, socket, head) => { | ||
wss.emit("connection", ws, request); | ||
this.setupClient(ws); | ||
this.rpc.setupClient(ws); | ||
}); | ||
@@ -632,223 +972,7 @@ }); | ||
} | ||
async executeCell(id, path2, cellId, force) { | ||
const startOK = (await Promise.all( | ||
Array.from(this.clients.values()).map( | ||
(client) => client.startCellExecution(path2, cellId, force) | ||
) | ||
)).every((ok) => ok); | ||
if (!startOK) { | ||
return false; | ||
} | ||
let data; | ||
let mime; | ||
const stdoutChunks = []; | ||
const stderrChunks = []; | ||
try { | ||
const cell = getCellById(this.cellsByPath, id); | ||
if (!cell) | ||
throw new Error(`cell not found: ${id}`); | ||
if (!cell.sourceDescription) { | ||
const [_, path3] = cellIdRegex.exec(id); | ||
cell.sourceDescription = rewrite_default( | ||
cell.code, | ||
cell.language, | ||
id, | ||
cell.cellId, | ||
this.cellsByPath.get(path3) | ||
); | ||
} | ||
if (cell.sourceDescription.type === "client") { | ||
data = JSON.stringify({ | ||
// TODO(jaked) strip workspace root when executeCell is called | ||
id: id.substring(this.viteServer.config.root.length + 1), | ||
origin: this.viteServer.config.server.origin | ||
}); | ||
mime = "application/x-vitale"; | ||
} else { | ||
const domain = Domain.create(); | ||
domain.stdoutWrite = (chunk) => { | ||
stdoutChunks.push(chunk); | ||
}; | ||
domain.stderrWrite = (chunk) => { | ||
stderrChunks.push(chunk); | ||
}; | ||
let { default: result } = await domain.run( | ||
async () => await this.viteRuntime.executeUrl(id) | ||
); | ||
if (result instanceof Promise) | ||
result = await result; | ||
if (typeof result === "object" && "data" in result && typeof result.data === "string" && "mime" in result && typeof result.mime === "string") { | ||
mime = result.mime; | ||
data = result.data; | ||
} else if (isSVGElementLike(result)) { | ||
mime = "image/svg+xml"; | ||
data = result.outerHTML; | ||
} else if (isHTMLElementLike(result)) { | ||
mime = "text/html"; | ||
data = result.outerHTML; | ||
} else if (typeof result === "object") { | ||
mime = "application/json"; | ||
data = JSON.stringify(result); | ||
} else { | ||
mime = "text/x-javascript"; | ||
data = JSON.stringify(result); | ||
} | ||
} | ||
} catch (e) { | ||
const err = e; | ||
const obj = { | ||
name: err.name, | ||
message: err.message, | ||
stack: rewriteStack(err.stack) | ||
}; | ||
data = JSON.stringify(obj, void 0, " "); | ||
mime = "application/vnd.code.notebook.error"; | ||
} | ||
const items = []; | ||
if (data !== void 0) { | ||
items.push({ data: [...Buffer.from(data, "utf8").values()], mime }); | ||
} | ||
if (stdoutChunks.length > 0) { | ||
items.push({ | ||
data: [...Buffer.concat(stdoutChunks).values()], | ||
mime: "application/vnd.code.notebook.stdout" | ||
}); | ||
} | ||
if (stderrChunks.length > 0) { | ||
items.push({ | ||
data: [...Buffer.concat(stderrChunks).values()], | ||
mime: "application/vnd.code.notebook.stderr" | ||
}); | ||
} | ||
const cellOutput = { items }; | ||
await Promise.all( | ||
Array.from(this.clients.values()).map( | ||
(client) => client.endCellExecution(path2, cellId, cellOutput) | ||
) | ||
); | ||
return true; | ||
} | ||
invalidateModule(id, dirtyCells) { | ||
const mod = this.viteRuntime.moduleCache.get(id); | ||
this.viteRuntime.moduleCache.delete(id); | ||
const match = cellIdRegex.exec(id); | ||
if (match) { | ||
const [_, path2, cellId, ext] = match; | ||
if (!dirtyCells.some((cell) => cell.path === path2 && cell.cellId === cellId)) { | ||
dirtyCells.push({ path: path2, cellId, ext }); | ||
} | ||
} | ||
for (const dep of mod.importers ?? []) { | ||
this.invalidateModule( | ||
Path.join(this.viteServer.config.root, dep), | ||
dirtyCells | ||
); | ||
} | ||
} | ||
markCellsDirty(cells) { | ||
if (cells.length === 0) { | ||
return; | ||
} | ||
for (const client of this.clients.values()) { | ||
client.markCellsDirty(cells); | ||
} | ||
} | ||
invalidateModuleAndDirty(id) { | ||
const cells = []; | ||
this.invalidateModule(id, cells); | ||
this.markCellsDirty(cells); | ||
this.runtime.invalidateRuntimeModule(id, cells); | ||
this.rpc.markCellsDirty(cells); | ||
} | ||
async executeCellsRPC(cells, force, executeDirtyCells) { | ||
let dirtyCells = []; | ||
for (const { path: path2, cellId, language, code } of cells) { | ||
const ext = extOfLanguage(language); | ||
const id = `${path2}-cellId=${cellId}.${ext}`; | ||
if (code) { | ||
setCellById(this.cellsByPath, id, { cellId, code, language }); | ||
} | ||
const mod = this.viteServer.moduleGraph.getModuleById(id); | ||
if (mod) { | ||
this.viteServer.moduleGraph.invalidateModule(mod); | ||
handleHMRUpdate(id, this.viteServer); | ||
} | ||
this.invalidateModule(id, dirtyCells); | ||
} | ||
dirtyCells = dirtyCells.filter( | ||
(dirtyCell) => !cells.some( | ||
(cell) => cell.path === dirtyCell.path && cell.cellId === dirtyCell.cellId | ||
) | ||
); | ||
const cellsToExecute = [ | ||
...cells.map(({ path: path2, cellId, language }) => ({ | ||
path: path2, | ||
cellId, | ||
ext: extOfLanguage(language), | ||
force | ||
})), | ||
...executeDirtyCells ? dirtyCells.map((cell) => ({ ...cell, force: false })) : [] | ||
]; | ||
const executed = await Promise.all( | ||
cellsToExecute.map( | ||
({ path: path2, cellId, ext, force: force2 }) => this.executeCell(`${path2}-cellId=${cellId}.${ext}`, path2, cellId, force2) | ||
) | ||
); | ||
const cellsToMarkDirty = cellsToExecute.filter( | ||
({ force: force2 }, i) => !force2 && !executed[i] | ||
); | ||
this.markCellsDirty(cellsToMarkDirty); | ||
} | ||
removeCellsRPC(cells) { | ||
let dirtyCells = []; | ||
for (const { path: path2, cellId, language } of cells) { | ||
const ext = extOfLanguage(language); | ||
const id = `${path2}-cellId=${cellId}.${ext}`; | ||
deleteCellById(this.cellsByPath, id); | ||
const mod = this.viteServer.moduleGraph.getModuleById(id); | ||
if (mod) { | ||
this.viteServer.moduleGraph.invalidateModule(mod); | ||
} | ||
this.invalidateModule(id, dirtyCells); | ||
} | ||
dirtyCells = dirtyCells.filter( | ||
(dirtyCell) => !cells.some( | ||
(cell) => cell.path === dirtyCell.path && cell.cellId === dirtyCell.cellId | ||
) | ||
); | ||
this.markCellsDirty(dirtyCells); | ||
} | ||
setupClient(ws) { | ||
const self = this; | ||
const rpc = createBirpc( | ||
{ | ||
ping: async () => { | ||
console.log("ping"); | ||
return "pong"; | ||
}, | ||
async executeCells(cells, force, executeDirtyCells) { | ||
try { | ||
return self.executeCellsRPC(cells, force, executeDirtyCells); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
}, | ||
async removeCells(cells) { | ||
try { | ||
return self.removeCellsRPC(cells); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
}, | ||
{ | ||
post: (msg) => ws.send(msg), | ||
on: (fn) => ws.on("message", fn), | ||
serialize: (v) => JSON5.stringify(v), | ||
deserialize: (v) => JSON5.parse(v) | ||
} | ||
); | ||
this.clients.set(ws, rpc); | ||
ws.on("close", () => { | ||
this.clients.delete(ws); | ||
}); | ||
} | ||
listen() { | ||
@@ -855,0 +979,0 @@ this.viteServer.listen(); |
@@ -28,3 +28,6 @@ interface CellOutputItem { | ||
startCellExecution: (path: string, cellId: string, force: boolean) => Promise<boolean>; | ||
endCellExecution: (path: string, cellId: string, cellOutput: CellOutput) => void; | ||
outputStdout: (path: string, cellId: string, output: string) => void; | ||
outputStderr: (path: string, cellId: string, output: string) => void; | ||
updateCellOutput: (path: string, cellId: string, cellOutput: CellOutput) => void; | ||
endCellExecution: (path: string, cellId: string, cellOutput?: CellOutput) => void; | ||
}; | ||
@@ -36,2 +39,18 @@ | ||
}; | ||
declare function textHtml(data: string): { | ||
data: string; | ||
mime: string; | ||
}; | ||
declare function textJson(data: string): { | ||
data: string; | ||
mime: string; | ||
}; | ||
declare function textMarkdown(data: string): { | ||
data: string; | ||
mime: string; | ||
}; | ||
declare function stream(data: string): { | ||
data: string; | ||
mime: string; | ||
}; | ||
declare function markdown(data: string): { | ||
@@ -62,2 +81,2 @@ data: string; | ||
export { type CellOutput, type CellOutputItem, type ClientFunctions, type ServerFunctions, html, json, jsonView, markdown, svg, text }; | ||
export { type CellOutput, type CellOutputItem, type ClientFunctions, type ServerFunctions, html, json, jsonView, markdown, stream, svg, text, textHtml, textJson, textMarkdown }; |
@@ -5,2 +5,14 @@ // src/index.ts | ||
} | ||
function textHtml(data) { | ||
return { data, mime: "text/x-html" }; | ||
} | ||
function textJson(data) { | ||
return { data, mime: "text/x-json" }; | ||
} | ||
function textMarkdown(data) { | ||
return { data, mime: "text/x-markdown" }; | ||
} | ||
function stream(data) { | ||
return { data, mime: "application/x.notebook.stream" }; | ||
} | ||
function markdown(data) { | ||
@@ -28,5 +40,9 @@ return { data, mime: "text/markdown" }; | ||
markdown, | ||
stream, | ||
svg, | ||
text | ||
text, | ||
textHtml, | ||
textJson, | ||
textMarkdown | ||
}; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@githubnext/vitale", | ||
"version": "0.0.14", | ||
"version": "0.0.15", | ||
"description": "", | ||
@@ -31,3 +31,3 @@ "license": "MIT", | ||
"vite": "^5.2.6", | ||
"ws": "^8.16.0" | ||
"ws": "^8.17.1" | ||
}, | ||
@@ -34,0 +34,0 @@ "devDependencies": { |
33982
13.14%1093
16.15%Updated