npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue2-sfc-compiler

v0.1.1

Published

Vue 2 SFC compiler with script setup, TypeScript, and style preprocessor support

Readme

vue2-sfc-compiler

Vue 2 单文件组件(SFC)编译器,支持 <script setup>、TypeScript、JSX 和样式预处理器。

致谢:本项目基于 Vue.js 团队的 @vue/compiler-sfc 构建。感谢 Vue 团队提供了优秀的 SFC 编译基础设施,使得在浏览器环境编译 Vue 2 SFC 成为可能。

作用与目的

将 Vue 2 SFC 源码编译为可执行的 JavaScript 和 CSS。核心能力:

  • 解析 SFC:拆分 <template><script><style>
  • 编译 Script:支持 <script setup>、TypeScript、JSX
  • 处理模板:将模板附加为字符串,由 Vue 运行时编译
  • 编译样式:支持 scoped CSS、Less、SCSS/SASS
  • 输出格式:ESM、CommonJS 或 UMD

本包是环境无关的纯编译器,通过依赖注入支持 Node.js 和浏览器环境。

包结构

vue2-sfc-compiler/
├── src/
│   ├── index.ts              # 入口,导出 createCompiler 等
│   ├── types.ts              # 类型定义
│   ├── parser.ts             # SFC 解析器
│   ├── compiler/             # 编译器模块
│   │   ├── index.ts          # 编译器入口
│   │   ├── script.ts         # Script 块编译
│   │   ├── template.ts       # Template 块处理
│   │   └── style.ts          # Style 块编译
│   └── transform/            # 代码转换
│       ├── index.ts          # 转换入口
│       └── umd.ts            # UMD 格式转换
├── dist/                     # 构建产物
├── package.json
└── tsup.config.ts

文件说明

| 文件/目录 | 作用 | |-----------|------| | index.ts | 包入口,导出 createCompilerparseSFCtoUMD 等 | | types.ts | TypeScript 类型定义(Compiler、CompileResult 等) | | parser.ts | 使用 @vue/compiler-sfc 解析 SFC,生成 SFCDescriptor | | compiler/script.ts | 编译 <script><script setup>,处理 JSX/TS | | compiler/template.ts | 将 template 内容附加到组件对象 | | compiler/style.ts | 编译样式,处理 scoped、预处理器 | | transform/umd.ts | 将 CommonJS 模块转换为 UMD 格式 |

使用场景

  • 在线编辑器:实时编译用户输入的 SFC 代码
  • 构建工具插件:自定义 Vue 2 SFC 编译流程
  • 代码生成:将 SFC 编译为可独立运行的模块

使用方法

1. 创建编译器

编译器需要注入 Babel 转换函数,以支持不同环境:

import { createCompiler } from 'vue2-sfc-compiler'

// 浏览器环境(需先加载 @babel/standalone)
const compiler = createCompiler({
  babelTransform: (code, options) => Babel.transform(code, options).code,
})

// Node.js 环境
import { transformSync } from '@babel/core'
const compiler = createCompiler({
  babelTransform: (code, options) => transformSync(code, options)?.code || '',
})

2. 编译 SFC

const sfcCode = `
<template>
  <div>{{ msg }}</div>
</template>

<script setup>
import { ref } from 'vue'
const msg = ref('Hello')
</script>

<style scoped>
div { color: red; }
</style>
`

// name 参数:组件名,用于 Vue devtools、scoped CSS ID、UMD 全局变量
const result = await compiler.compileSFC(sfcCode, 'MyComponent')

console.log(result.js)      // 编译后的 JavaScript
console.log(result.css)     // 编译后的 CSS(含 scoped 处理)
console.log(result.errors)  // 编译错误数组
console.log(result.name)    // 组件名 "MyComponent"

3. 编译为 CommonJS(沙箱执行)

// 编译 SFC 为 CommonJS 格式,用于沙箱/iframe 执行
const result = await compiler.compileToCommonJS(sfcCode, 'MyComponent')

if (result.errors.length === 0) {
  // 在沙箱中执行,使用自定义 require()
  const module = { exports: {} }
  const require = (id) => {
    if (id === 'vue') return Vue
    // ... 处理其他依赖
  }
  new Function('require', 'module', 'exports', result.js)(require, module, module.exports)
  const Component = module.exports.default
}

// result.js - CommonJS 代码
// result.css - 编译后的 CSS
// result.errors - 编译错误

4. 生成 UMD(Script 标签加载)

// 高级 API:直接从 SFC 生成完整 UMD 组件
const result = await compiler.compileToUMD(sfcCode, 'MyButton')

// 检查编译错误
if (result.errors.length > 0) {
  console.error('编译错误:', result.errors)
}

// result.code - UMD 代码(含 CSS 自动注入)
// result.name - 组件名
// result.errors - 编译错误数组

// 使用:<script src="my-button.js"></script>
// 导出:window.MyButton

5. 分步转换为 UMD

// 先编译,再转换(适合需要中间结果的场景)
const result = await compiler.compileSFC(sfcCode, 'MyButton')
const umdCode = compiler.toUMD(result, {
  externals: { 'element-ui': 'ELEMENT' }
})

UMD 产物是完整的组件,包含:

  • ✅ JavaScript 组件逻辑
  • ✅ Template(已编译为字符串附加到组件)
  • ✅ CSS(自动注入到 <head>,带去重处理)

4. 配置样式预处理器

const compiler = createCompiler({
  babelTransform: ...,
  stylePreprocessors: {
    less: async (code) => {
      const result = await less.render(code)
      return result.css
    },
    scss: async (code) => {
      // SCSS 处理逻辑
    },
  },
})

编译流程

整体编译链

SFC 源码
    │
    ▼
┌──────────────────────────────────┐
│  @vue/compiler-sfc               │  解析 SFC,编译 <script setup>
│  输出: ESM (export default)      │
└──────────────────────────────────┘
    │
    ▼
┌──────────────────────────────────┐
│  vue2-jsx-browser (Babel 插件)   │  转换 JSX 语法
│  输出: h() 函数调用              │  <div>Hi</div> → h('div', 'Hi')
└──────────────────────────────────┘
    │
    ▼
┌──────────────────────────────────┐
│  compileSFC                      │  组合以上步骤 + 样式处理
│  输出: ESM + CSS                 │  CompileResult { js, css, errors, name }
└──────────────────────────────────┘
    │
    ├─────────────────────────────────┐
    ▼                                 ▼
┌─────────────────────┐    ┌─────────────────────────┐
│  compileToCommonJS  │    │  compileToUMD           │
│  输出: CJS + CSS    │    │  输出: UMD + CSS        │
│  (沙箱执行用)       │    │  (Script 标签加载)      │
└─────────────────────┘    └─────────────────────────┘

输出格式

| 方法 | 格式 | 使用场景 | |------|------|----------| | compileSFC | ESM | 构建工具、打包器 | | compileToCommonJS | CommonJS | 沙箱/iframe 执行 | | compileToUMD | UMD | Script 标签加载 |

compileSFC 内部流程

SFC 源码
    │
    ▼
┌─────────────┐
│   parser    │  解析为 SFCDescriptor(使用 @vue/compiler-sfc)
└─────────────┘
    │
    ▼
┌─────────────┐
│   script    │  编译 <script> / <script setup>,处理 JSX/TS
└─────────────┘
    │
    ▼
┌─────────────┐
│  template   │  附加模板字符串到组件(运行时编译)
└─────────────┘
    │
    ▼
┌─────────────┐
│   style     │  编译样式,处理 scoped、Less/SCSS
└─────────────┘
    │
    ▼
CompileResult { js, css, errors, name }

依赖关系

内置依赖(已打包)

| 依赖 | 作用 | |------|------| | @vue/compiler-sfc | 解析 SFC,编译 <script setup> | | vue2-jsx-browser | JSX 语法转换(Babel 插件) |

为什么使用 Vue 3 的 @vue/compiler-sfc?

本包使用 Vue 3 的 @vue/compiler-sfc(v3.5+)而不是 Vue 2.7 的版本,因为 Vue 2.7 的 compiler-sfc 无法在浏览器中运行

| | Vue 2.7 compiler-sfc | Vue 3 compiler-sfc | |---|---|---| | 浏览器支持 | ❌ 仅 Node.js | ✅ 浏览器兼容 | | 模块格式 | 仅 CommonJS | ESM + CommonJS + ESM Browser | | Node.js API | 使用 pathurl 等 | 浏览器安全 | | 依赖数量 | 40+ 可选依赖(consolidate.js) | 依赖极少 |

关键点:Vue 3 编译器的输出 完全兼容 Vue 2.7 运行时。我们将模板作为字符串附加,由 Vue 2 运行时编译,确保 100% Vue 2 语法兼容。

查看详细说明 了解技术细节和尝试过的解决方案。

注入依赖(用户提供)

| 依赖 | 注入方式 | 作用 | |------|----------|------| | Babel | babelTransform | 代码转换引擎(必需) | | TypeScript preset | Babel preset | 编译 TS/TSX | | Less/SCSS | stylePreprocessors | 样式预处理(可选) |

Node.js 环境

# 安装依赖
npm install @babel/core @babel/preset-typescript  # 必需
npm install less sass                              # 可选
import { createCompiler } from 'vue2-sfc-compiler'
import { transformSync } from '@babel/core'
import less from 'less'        // 可选
import * as sass from 'sass'   // 可选

const compiler = createCompiler({
  // Babel 转换(必需)
  // Node.js 需要显式添加 TypeScript preset
  babelTransform: (code, options) => {
    const result = transformSync(code, {
      ...options,
      presets: [
        ...(options.presets || []),
        ['@babel/preset-typescript', { isTSX: true, allExtensions: true }],
      ],
    })
    return result?.code || ''
  },

  // 样式预处理器(可选)
  stylePreprocessors: {
    less: async (code) => {
      const result = await less.render(code)
      return result.css
    },
    scss: (code) => {
      const result = sass.compileString(code)
      return result.css
    },
    sass: (code) => {
      const result = sass.compileString(code, { syntax: 'indented' })
      return result.css
    },
  },
})

浏览器环境

<!-- Babel standalone(必需),内置 TypeScript preset -->
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>

<!-- Less(可选) -->
<script src="https://cdn.jsdelivr.net/npm/less@4"></script>

<!-- Sass(可选),浏览器端较复杂,建议使用 Less -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/sass.sync.js"></script>
import { createCompiler } from 'vue2-sfc-compiler'

// 类型声明
declare const Babel: { transform: (code: string, options: unknown) => { code: string } }
declare const less: { render: (code: string) => Promise<{ css: string }> }
declare const Sass: { compile: (code: string) => string }

const compiler = createCompiler({
  // Babel 转换(必需)
  // @babel/standalone 内置 TypeScript preset,无需额外配置
  babelTransform: (code, options) => {
    return Babel.transform(code, options).code
  },

  // 样式预处理器(可选)
  stylePreprocessors: {
    less: async (code) => {
      const result = await less.render(code)
      return result.css
    },
    scss: (code) => {
      return Sass.compile(code)
    },
  },
})