Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: run Slidev in browser #1800

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion netlify.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[build]
publish = "docs/.vitepress/dist"
command = "pnpm run docs:build"
command = "pnpm run website:build"

[build.environment]
NODE_VERSION = "20"
Expand Down Expand Up @@ -28,6 +28,11 @@ from = "/demo/vue-runner/*"
to = "/demo/vue-runner/index.html"
status = 200

[[redirects]]
from = "/web/*"
to = "/web/index.html"
status = 200

[[redirects]]
from = "https://slidev.antfu.me/*"
to = "https://sli.dev/:splat"
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
"lint:fix": "nr lint --fix",
"typecheck": "vue-tsc --noEmit",
"docs": "pnpm -C docs run dev",
"docs:build": "pnpm run --filter {./docs}... build && pnpm demo:build",
"docs:build": "pnpm run --filter {./docs}... build",
"web": "pnpm -C packages/web run dev",
"web:build": "zx ./scripts/web.mjs",
"website:build": "pnpm docs:build && pnpm demo:build && pnpm web:build",
"release": "bumpp package.json packages/*/package.json docs/package.json --all -x \"zx scripts/update-versions.mjs\"",
"test": "vitest test",
"prepare": "simple-git-hooks"
Expand Down
2 changes: 1 addition & 1 deletion packages/client/logic/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { parseRangeString } from '@slidev/parser/core'
import { parseRangeString } from '@slidev/parser/utils'
import { useTimestamp } from '@vueuse/core'
import { computed, ref } from 'vue'

Expand Down
6 changes: 5 additions & 1 deletion packages/parser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
"./fs": {
"types": "./dist/fs.d.mts",
"import": "./dist/fs.mjs"
},
"./utils": {
"types": "./dist/utils.d.mts",
"import": "./dist/utils.mjs"
}
},
"main": "dist/index.mjs",
Expand All @@ -36,7 +40,7 @@
"node": ">=18.0.0"
},
"scripts": {
"build": "rimraf dist && tsup src/index.ts src/core.ts src/fs.ts",
"build": "rimraf dist && tsup src/index.ts src/core.ts src/fs.ts src/utils.ts",
"dev": "nr build --watch",
"prepublishOnly": "npm run build"
},
Expand Down
6 changes: 4 additions & 2 deletions packages/slidev/node/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getThemeMeta, resolveTheme } from './integrations/themes'
import { resolveAddons } from './integrations/addons'
import { getRoots, resolveEntry } from './resolver'
import setupShiki from './setups/shiki'
import { getDefine } from './vite/extendConfig'

const debug = Debug('slidev:options')

Expand Down Expand Up @@ -61,13 +62,13 @@ export async function resolveOptions(
themeRoots,
addonRoots,
roots,
utils: await createDataUtils(data, rootsInfo.clientRoot, roots),
utils: await createDataUtils(data, rootsInfo.clientRoot, roots, mode),
}

return resolved
}

export async function createDataUtils(data: SlidevData, clientRoot: string, roots: string[]): Promise<ResolvedSlidevUtils> {
export async function createDataUtils(data: SlidevData, clientRoot: string, roots: string[], mode: string): Promise<ResolvedSlidevUtils> {
const monacoTypesIgnorePackagesMatches = (data.config.monacoTypesIgnorePackages || [])
.map(i => mm.matcher(i))

Expand All @@ -76,6 +77,7 @@ export async function createDataUtils(data: SlidevData, clientRoot: string, root

return {
...await setupShiki(roots),
defines: getDefine({ data, clientRoot, mode } as any),
isMonacoTypesIgnored: pkg => monacoTypesIgnorePackagesMatches.some(i => i(pkg)),
getLayouts: () => {
const now = Date.now()
Expand Down
3 changes: 1 addition & 2 deletions packages/slidev/node/vite/compilerFlagsVue.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import type { Plugin } from 'vite'
import { objectEntries } from '@antfu/utils'
import type { ResolvedSlidevOptions } from '@slidev/types'
import { getDefine } from './extendConfig'

/**
* Replace compiler flags like `__DEV__` in Vue SFC
*/
export function createVueCompilerFlagsPlugin(
options: ResolvedSlidevOptions,
): Plugin[] {
const define = objectEntries(getDefine(options))
const define = objectEntries(options.utils.defines)
return [
{
name: 'slidev:flags',
Expand Down
2 changes: 1 addition & 1 deletion packages/slidev/node/vite/extendConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export function createConfigPlugin(options: ResolvedSlidevOptions): Plugin {
name: 'slidev:config',
async config(config) {
const injection: InlineConfig = {
define: getDefine(options),
define: options.utils.defines,
resolve: {
alias: [
{
Expand Down
10 changes: 7 additions & 3 deletions packages/slidev/node/vite/layoutWrapper.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import type { ResolvedSlidevOptions } from '@slidev/types'
import type { Plugin } from 'vite'
import { bold, gray, red, yellow } from 'kolorist'
import { toAtFS } from '../resolver'
import { ensurePrefix, slash } from '@antfu/utils'
import { regexSlideSourceId, templateImportContextUtils, templateInitContext, templateInjectionMarker } from './common'

export function toAtFS(path: string) {
return `/@fs${ensurePrefix('/', slash(path))}`
}

export function createLayoutWrapperPlugin(
{ data, utils }: ResolvedSlidevOptions,
): Plugin {
return {
name: 'slidev:layout-wrapper',
async transform(code, id) {
transform(code, id) {
const match = id.match(regexSlideSourceId)
if (!match)
return
const [, no, type] = match
if (type !== 'md')
return
const index = +no - 1
const layouts = await utils.getLayouts()
const layouts = utils.getLayouts()
const rawLayoutName = data.slides[index]?.frontmatter?.layout ?? data.slides[0]?.frontmatter?.default?.layout
let layoutName = rawLayoutName || (index === 0 ? 'cover' : 'default')
if (!layouts[layoutName]) {
Expand Down
2 changes: 1 addition & 1 deletion packages/slidev/node/vite/loaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export function createSlidesLoader(
}

Object.assign(data, newData)
Object.assign(utils, createDataUtils(newData, clientRoot, roots))
Object.assign(utils, createDataUtils(newData, clientRoot, roots, mode))

if (hmrSlidesIndexes.size > 0)
moduleIds.add(templateTitleRendererMd.id)
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ResolvedSlidevOptions extends RootsInfo, SlidevEntryOptions {
export interface ResolvedSlidevUtils {
shiki: HighlighterGeneric<any, any>
shikiOptions: MarkdownItShikiOptions
defines: Record<string, string>
isMonacoTypesIgnored: (pkg: string) => boolean
getLayouts: () => Record<string, string>
}
Expand Down
15 changes: 15 additions & 0 deletions packages/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://cdn.jsdelivr.net/gh/slidevjs/slidev/assets/favicon.png">
<title>Slidev Online</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="src/main.ts"></script>
<div id="mermaid-rendering-container"></div>
<!-- body -->
</body>
</html>
27 changes: 27 additions & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@slidev/web",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"@antfu/utils": "^0.7.10",
"@babel/standalone": "^7.25.3",
"@slidev/client": "workspace:*",
"@slidev/parser": "workspace:*",
"@slidev/theme-seriph": "^0.25.0",
"@types/babel__standalone": "^7.1.7",
"@vue/babel-plugin-jsx": "^1.2.2",
"magic-string": "^0.30.10",
"ohash": "^1.1.3",
"pathe": "^1.1.2",
"sucrase": "^3.35.0",
"typescript": "^5.5.4",
"unocss": "^0.61.5",
"unplugin-vue-markdown": "^0.26.2",
"vite": "^5.3.4",
"vue": "^3.4.33",
"vue-router": "^4.4.0"
}
}
2 changes: 2 additions & 0 deletions packages/web/polyfills/node-assert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export default function () {
}
1 change: 1 addition & 0 deletions packages/web/polyfills/node-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const createRequire = () => null
1 change: 1 addition & 0 deletions packages/web/polyfills/rollup-pluginutils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const createFilter = () => () => true
1 change: 1 addition & 0 deletions packages/web/polyfills/unplugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const createUnplugin = () => null
65 changes: 65 additions & 0 deletions packages/web/src/compiler/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Ported from https://github.com/vuejs/repl/blob/main/src/transform.ts

import { compileMd } from './md'
import { transformTS } from './ts'
import { isJsLikeFile, isJsxFile, isTsFile } from './utils'
import { compileVue } from './vue'

export interface CompileResult {
js?: string
css?: string
errors?: (string | Error)[]
}

export async function compileFile(filepath: string, code: string): Promise<CompileResult> {
if (!code.trim()) {
return {}
}

if (filepath.endsWith('.css')) {
return {
css: code,
}
}

if (isJsLikeFile(filepath)) {
const isJSX = isJsxFile(filepath)
if (isTsFile(filepath)) {
code = await transformTS(code, isJSX)
}
if (isJSX) {
code = await import('./jsx').then(m => m.transformJSX(code))
}
return {
js: code,
}
}

if (filepath.endsWith('.json')) {
let parsed
try {
parsed = JSON.parse(code)
}
catch (err: any) {
console.error(`Error parsing ${filepath}`, err.message)
return {
errors: [err.message],
}
}
return {
js: `export default ${JSON.stringify(parsed)}`,
}
}

if (filepath.endsWith('.vue')) {
return compileVue(filepath, code)
}

if (filepath.endsWith('.md')) {
return compileMd(filepath, code)
}

return {
errors: [`Unknown file extension for ${filepath}`],
}
}
10 changes: 10 additions & 0 deletions packages/web/src/compiler/jsx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Ported from https://github.com/vuejs/repl/blob/main/src/jsx.ts

import { transform } from '@babel/standalone'
import jsx from '@vue/babel-plugin-jsx'

export async function transformJSX(src: string) {
return transform(src, {
plugins: [jsx],
}).code!
}
Loading
Loading