Skip to content

Commit 9f0aa67

Browse files
authored
feat: support async output in code runner (#1685)
1 parent ac65d94 commit 9f0aa67

File tree

3 files changed

+28
-26
lines changed

3 files changed

+28
-26
lines changed

packages/client/internals/CodeRunner.vue

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<script setup lang="ts">
22
import { debounce, toArray } from '@antfu/utils'
33
import { useVModel } from '@vueuse/core'
4-
import type { CodeRunnerOutput, RawAtValue } from '@slidev/types'
5-
import { computed, onMounted, onUnmounted, ref, shallowRef, watch, watchSyncEffect } from 'vue'
4+
import type { CodeRunnerOutputs, RawAtValue } from '@slidev/types'
5+
import { computed, onMounted, onUnmounted, ref, shallowRef, toValue, watch, watchSyncEffect } from 'vue'
66
import { useSlideContext } from '../context'
77
import setupCodeRunners from '../setup/code-runners'
88
import { useNav } from '../composables/useNav'
@@ -31,7 +31,7 @@ const disabled = computed(() => !['slide', 'presenter'].includes($renderContext.
3131
3232
const autorun = isPrintMode.value ? 'once' : props.autorun
3333
const isRunning = ref(autorun)
34-
const outputs = shallowRef<CodeRunnerOutput[]>()
34+
const outputs = shallowRef<CodeRunnerOutputs>()
3535
const runCount = ref(0)
3636
const highlightFn = ref<(code: string, lang: string) => string>()
3737
@@ -64,7 +64,7 @@ const triggerRun = debounce(200, async () => {
6464
isRunning.value = true
6565
}, 500)
6666
67-
outputs.value = toArray(await run(code.value, props.lang, props.runnerOptions ?? {}))
67+
outputs.value = await run(code.value, props.lang, props.runnerOptions ?? {})
6868
runCount.value += 1
6969
isRunning.value = false
7070
@@ -90,11 +90,11 @@ else if (autorun)
9090
<div v-else-if="isRunning" class="text-sm text-center opacity-50">
9191
Running...
9292
</div>
93-
<div v-else-if="!outputs?.length" class="text-sm text-center opacity-50">
93+
<div v-else-if="!outputs" class="text-sm text-center opacity-50">
9494
Click the play button to run the code
9595
</div>
9696
<div v-else :key="`run-${runCount}`" class="slidev-runner-output">
97-
<template v-for="output, _idx1 of outputs" :key="_idx1">
97+
<template v-for="output, _idx1 of toArray(toValue(outputs))" :key="_idx1">
9898
<div v-if="'html' in output" v-html="output.html" />
9999
<div v-else-if="'error' in output" class="text-red-500">
100100
{{ output.error }}

packages/client/setup/code-runners.ts

+20-19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createSingletonPromise } from '@antfu/utils'
22
import type { CodeRunner, CodeRunnerOutput, CodeRunnerOutputText, CodeRunnerOutputs } from '@slidev/types'
33
import type { CodeToHastOptions } from 'shiki'
44
import type ts from 'typescript'
5+
import { ref } from 'vue'
56
import { isDark } from '../logic/dark'
67
import deps from '#slidev/monaco-run-deps'
78
import setups from '#slidev/setups/code-runners'
@@ -62,32 +63,32 @@ export default createSingletonPromise(async () => {
6263
})
6364

6465
// Ported from https://github.com/microsoft/TypeScript-Website/blob/v2/packages/playground/src/sidebar/runtime.ts
65-
async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
66-
const allLogs: CodeRunnerOutput[] = []
67-
68-
const replace = {} as any
69-
const logger = function (...objs: any[]) {
70-
allLogs.push(objs.map(printObject))
71-
}
72-
replace.info = replace.log = replace.debug = replace.warn = replace.error = logger
73-
replace.clear = () => allLogs.length = 0
74-
const vmConsole = Object.assign({}, console, replace)
66+
function runJavaScript(code: string): CodeRunnerOutputs {
67+
const result = ref<CodeRunnerOutput[]>([])
68+
69+
const onError = (error: any) => result.value.push({ error: String(error) })
70+
const logger = (...objs: any[]) => result.value.push(objs.map(printObject))
71+
const vmConsole = Object.assign({}, console)
72+
vmConsole.info = vmConsole.log = vmConsole.debug = vmConsole.warn = vmConsole.error = logger
73+
vmConsole.clear = () => result.value.length = 0
7574
try {
76-
const safeJS = `return async (console, __slidev_import) => {
77-
${sanitizeJS(code)}
75+
const safeJS = `return async (console, __slidev_import, __slidev_on_error) => {
76+
try {
77+
${sanitizeJS(code)}
78+
} catch (e) {
79+
__slidev_on_error(e)
80+
}
7881
}`
7982
// eslint-disable-next-line no-new-func
80-
await (new Function(safeJS)())(vmConsole, (specifier: string) => {
83+
;(new Function(safeJS)())(vmConsole, (specifier: string) => {
8184
const mod = deps[specifier]
8285
if (!mod)
8386
throw new Error(`Module not found: ${specifier}.\nAvailable modules: ${Object.keys(deps).join(', ')}. Please refer to https://sli.dev/custom/config-code-runners#additional-runner-dependencies`)
8487
return mod
85-
})
88+
}, onError)
8689
}
8790
catch (error) {
88-
return {
89-
error: String(error),
90-
}
91+
onError(error)
9192
}
9293

9394
function printObject(arg: any): CodeRunnerOutputText {
@@ -162,7 +163,7 @@ async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
162163
return code
163164
}
164165

165-
return allLogs
166+
return result
166167
}
167168

168169
let tsModule: typeof import('typescript') | undefined
@@ -183,7 +184,7 @@ export async function runTypeScript(code: string) {
183184
const importRegex = /import\s*\((.+)\)/g
184185
code = code.replace(importRegex, (_full, specifier) => `__slidev_import(${specifier})`)
185186

186-
return await runJavaScript(code)
187+
return runJavaScript(code)
187188
}
188189

189190
/**

packages/types/src/code-runner.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { CodeToHastOptions } from 'shiki'
22
import type { Arrayable, Awaitable } from '@antfu/utils'
3+
import type { MaybeRefOrGetter } from 'vue'
34

45
export interface CodeRunnerContext {
56
/**
@@ -58,7 +59,7 @@ export type CodeRunnerOutputTextArray = CodeRunnerOutputText[]
5859

5960
export type CodeRunnerOutput = CodeRunnerOutputHtml | CodeRunnerOutputError | CodeRunnerOutputText | CodeRunnerOutputTextArray | CodeRunnerOutputDom
6061

61-
export type CodeRunnerOutputs = Arrayable<CodeRunnerOutput>
62+
export type CodeRunnerOutputs = MaybeRefOrGetter<Arrayable<CodeRunnerOutput>>
6263

6364
export type CodeRunner = (code: string, ctx: CodeRunnerContext) => Awaitable<CodeRunnerOutputs>
6465

0 commit comments

Comments
 (0)