为 Vue SFC REPL 添加 UnoCSS 支持,可用于 Vue + UnoCss 的 demo 集。
preview 部署在 vue-repl-two.vercel.app
简析原项目
这里只挑选核心的目录/文件。
- 📁codemirror - 编辑器的 codemirror 实现
- 📁monaco - 编辑器的 monaco editor 实现,包括 vue language service 的 worker
- 📁editor
- FileSelector.vue - 输入标签列表
- 📁output
- Output.vue - 输出面板
- Sandbox.vue - 预览沙盒
- srcdoc.html - 预览沙盒的 srcdoc
- store.ts - 源码文件、配置文件、编译器、编译输出等状态管理
编辑器输入源码和配置文件,通过 Vue SFC compiler 编译,输出并在 iframe 沙盒中预览。
改造方案
为了实现 UnoCSS 的输入、输出、预览,改造点如下:
- 添加
uno.config.ts
输入,在store.ts
中和tsconfig.json
、import map 一并管理,监听并编译uno.config.ts
,生成 config。 - 构造 UnoCSS 生成器;监听源码,根据输入源码生成
uno.css
,在Output.vue
添加输出标签 - 将
uno.css
插入到预览沙盒 iframe 的一个<style>
标签中。
上述内容中,输入输出的添加比较简单,本文略,下面说说 uno.config.ts
的在线编译。
完整代码可查看笔者的 fork。
在线编译 uno.config.ts
参考 UnoCSS Playground 实现,UnoCSS 编译的核心代码如下:
ts
import { $fetch } from 'ofetch'
export async function evaluateUserConfig<U = UserConfig>(
configStr: string,
version: string = 'latest',
): Promise<U | undefined> {
const modulesCache = globalCache
const moduleMap =
version === 'latest'
? unocssBundle
: new Map(
unocssBundle
.keys()
.map((p) => [
p,
() => import(/* @vite-ignore */ `https://esm.sh/${p}@${version}`),
]),
)
const code = configStr
.replace(
/import\s(.*?)\sfrom\s*(['"])unocss\2/g,
'const $1 = await __import("unocss");',
)
.replace(
/import\s*(\{[\s\S]*?\})\s*from\s*(['"])([\w@/-]+)\2/g,
'const $1 = await __import("$3");',
)
.replace(
/import\s(.*?)\sfrom\s*(['"])([\w@/-]+)\2/g,
'const $1 = (await __import("$3")).default;',
)
.replace(/export default /, 'return ')
.replace(/\bimport\s*\(/, '__import(')
// bypass vite interop
// eslint-disable-next-line no-new-func
const _import = new Function('a', 'return import(a);')
const __import = (name: string): any => {
if (!modulesCache.has(name)) {
modulesCache.set(
name,
moduleMap.has(name)
? moduleMap.get(name)!()
: name.endsWith('.json')
? $fetch(CDN_BASE + name, { responseType: 'json' }).then((r) => ({
default: r,
}))
: _import(CDN_BASE + name),
)
}
return modulesCache.get(name)
}
const fn = new AsyncFunction('__import', code)
const result = await fn(__import)
if (result) return result
}
evaluateUserConfig
读取 uno.config.ts
,将 import 语句解析并替换为预先准备好(或指定版本下载)的模块。
总结
本文简析了 Vue SFC REPL 项目,在此基础上通过在线编译 uno.config.ts
添加了UnoCSS 的输入、输出、预览,交互和原交互无缝融合。
下期预告: 现在的编辑器不支持 UnoCSS Token 的 hover、autocomplete、underline 等功能。下期加上。