@vue-macros/common
@vue-macros/common is the shared compiler-tooling layer inside the Vue Macros monorepo. It gives macro and bundler-plugin authors helpers for parsing Vue single-file components, obtaining Babel ASTs for script blocks, filtering Vue and setup files, injecting imports with MagicString, checking whether AST expressions can be hoisted, detecting the installed Vue version, and reading the Vite Vue plugin API. It also re-exports ast-kit and magic-string-ast. It does not add macros to an application by itself; the root README tells application developers to install vue-macros instead.
A productive foundation for authors who intentionally want Vue Macros' compiler conventions. Application developers and simple SFC parsers should install the higher-level suite or @vue/compiler-sfc instead.
Use it if
- You are implementing or maintaining a Vue compile-time macro and want the same SFC parsing and AST conventions as Vue Macros
- You are writing an unplugin or Vite transform that must recognize .vue, .setup.tsx, and Vue virtual script request IDs consistently
- You need to edit both normal script and script setup blocks while preserving source positions with MagicString
- Your tool supports Vue 2.7 and Vue 3 and needs package-root Vue version detection rather than a hard-coded version
- You only want to use Vue Macros features in an application; the project README says to install vue-macros, not this internal support package
- You only need to parse a .vue file; @vue/compiler-sfc exposes the underlying parser directly with less project-specific API surface
- Your runtime is below Node 20.19; version 3.1.4 declares node >=20.19.0 and is ESM-only
- You need a small browser runtime helper; this package pulls compiler-sfc, ast-kit, local-pkg, magic-string-ast, and unplugin-utils and is meant for build-time Node code
- You require independently versioned, fully documented public contracts; the official site documents Vue Macros features, while these common helpers are primarily documented by declarations and monorepo source
- You want stable behavior across arbitrary Vite Vue plugin versions: getVuePluginApi expects @vitejs/plugin-vue or unplugin-vue and its runtime rejects plugin APIs without a version, even though its declaration currently allows null
Setup reality
Install with npm install -D @vue-macros/common only when authoring compiler or build tooling. Version 3.1.4 is ESM-only, requires Node 20.19 or newer, and ships its declarations at dist/index.d.ts, so use import syntax from an ESM module. It has five runtime dependencies, including @vue/compiler-sfc 3.5, and declares Vue ^2.7 or ^3.2.25 as an optional peer. Optional means npm will not force Vue into a generic tool package; functions that inspect a project's Vue installation still need a resolvable Vue package at the root you pass. detectVueVersion uses Node's module resolution through process.getBuiltinModule and falls back to the supplied default with a console warning when resolution is unavailable. parseSFC parses structure immediately but its getScriptAst() and getSetupAst() methods parse JavaScript or TypeScript lazily; always inspect the returned errors array and handle the possibility that a requested block is absent. The filter helpers operate on bundler IDs, including framework-specific virtual query strings, so use the webpack or rspack framework argument where appropriate instead of applying the plain .vue regex everywhere. getVuePluginApi must run after plugins are resolved, requires the Vue plugin to appear before your plugin, and throws when it cannot find a supported API. The package re-exports all of ast-kit and magic-string-ast, which is convenient but expands the surface that may shift on a major upgrade. There is no standalone README in the published tarball, no config file, no native build, and no credentials; the real cost is keeping compiler, bundler, Vue, and Node versions aligned.
Patterns
Parse a Vue single-file componentparse-vue-sfc
import { parseSFC } from '@vue-macros/common'
const sfc = parseSFC(sourceCode, '/src/UserCard.vue')
if (sfc.errors.length) {
throw new AggregateError(sfc.errors, 'Invalid Vue SFC')
}
console.log(sfc.template?.content)
console.log(sfc.scriptSetup?.content)parseSFC returns compiler errors instead of automatically throwing for all malformed SFC input. Check errors before transforming blocks.
Get ASTs for normal and setup scriptsparse-script-asts
import { parseSFC } from '@vue-macros/common'
const sfc = parseSFC(code, id)
const normalProgram = sfc.getScriptAst()
const setupProgram = sfc.getSetupAst()
for (const statement of setupProgram?.body ?? []) {
inspectStatement(statement)
}The AST methods are lazy and return undefined when the corresponding block is absent. The parser uses the SFC's script language for both blocks.
Handle mixed script language errorsenforce-matching-script-languages
import { parseSFC } from '@vue-macros/common'
try {
const sfc = parseSFC(code, id)
transform(sfc)
} catch (error) {
if (String(error.message).includes('same language type')) {
report(id, 'Use the same lang on <script> and <script setup>')
} else {
throw error
}
}Unlike ordinary compiler parse errors, differing lang values on script and script setup cause parseSFC itself to throw.
Combine script blocks for whole-file analysisextract-script-code
import { getFileCodeAndLang } from '@vue-macros/common'
const result = getFileCodeAndLang(source, filename)
const program = analyzeModule(result.code, result.lang)
console.log(program.body.length)For .vue input, normal script and script setup content are joined with a semicolon. For .ts, .tsx, and other source files, the original code is returned.
Detect Vue from a project rootdetect-vue-version
import { detectVueVersion } from '@vue-macros/common'
const vueVersion = detectVueVersion('/workspace/app', 3.5)
const isVue2 = vueVersion >= 2 && vueVersion < 3
console.log({ vueVersion, isVue2 })Vue 2 versions are truncated to 2; Vue 3 keeps its parsed major/minor value. Missing resolution returns the default and may print a warning.
Create an include and exclude filterfilter-transform-files
import { createFilter } from '@vue-macros/common'
const shouldTransform = createFilter({
include: [/\.vue$/, /\.setup\.[jt]sx?$/],
exclude: [/node_modules/, /\.spec\.[jt]s$/],
})
if (shouldTransform(id)) {
return transform(code, id)
}The helper delegates to unplugin-utils. Bundler IDs can include query strings, so a hand-written end-anchored .vue regex may miss virtual subrequests.
Generate filters for Vue and setup filesbuild-framework-patterns
import {
FilterFileType,
getFilterPattern,
} from '@vue-macros/common'
const include = getFilterPattern([
FilterFileType.VUE_SFC_WITH_SETUP,
FilterFileType.SETUP_SFC,
], 'webpack')Pass webpack or rspack for their Vue virtual request patterns. Other framework values receive plain .vue and .setup source regexes.
Read the resolved Vite Vue plugin APIread-vue-plugin-api
import { getVuePluginApi } from '@vue-macros/common'
export default {
name: 'my-vue-transform',
configResolved(config) {
const vueApi = getVuePluginApi(config.plugins)
console.log(vueApi.version, vueApi.options)
},
}Place the Vue plugin before this plugin. The runtime throws if vite:vue or unplugin-vue is absent or exposes no supported API version.
Decide whether an AST value can be hoistedcheck-static-expression
import { babelParse, isStaticExpression } from '@vue-macros/common'
const ast = babelParse('const options = { eager: true, names: ["a"] }', 'ts')
const declaration = ast.body[0].declarations[0]
const canHoist = isStaticExpression(declaration.init, {
object: true,
array: true,
unary: true,
})
console.log(canHoist)Objects, arrays, functions, unary expressions, regular expressions, and object methods are opt-in. Identifier references are not static by default.
Map a static object expression by keyresolve-object-properties
import { babelParse, resolveObjectExpression } from '@vue-macros/common'
const ast = babelParse('const options = { name: "Card", inheritAttrs: false }', 'ts')
const objectNode = ast.body[0].declarations[0].init
const properties = resolveObjectExpression(objectNode)
console.log(properties?.name.type)
console.log(properties?.inheritAttrs.type)The input must already be known to be static. A spread of anything other than another object expression makes the helper return undefined.
Reject setup-local values in a hoisted macroreject-local-scope-reference
import { checkInvalidScopeReference } from '@vue-macros/common'
checkInvalidScopeReference(
macroCall.arguments[0],
'defineOptions',
[...setupBindingNames],
)The helper walks identifiers and throws SyntaxError on the first local binding it finds. Pass only actual setup bindings, not every imported or global name.
Insert and deduplicate a helper importinject-runtime-helper
import { MagicString, importHelperFn } from '@vue-macros/common'
const output = new MagicString(code)
const refName = importHelperFn(output, 0, 'ref')
const sameRefName = importHelperFn(output, 0, 'ref')
console.log(refName, sameRefName)
console.log(output.toString())The default local name is prefixed with __MACROS_. Calls are deduplicated per MagicString instance and exact source/import/local/prefix combination.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| vue-macros | npm | You are building a Vue application and want the finished macro suite rather than authoring compiler transforms |
| @vue/compiler-sfc | npm | You need the official SFC parser and compiler APIs without Vue Macros-specific helpers or constants |
| unplugin | npm | You need the cross-bundler plugin framework and will supply your own Vue parsing and transform conventions |
| ast-kit | npm | Your work is mainly Babel AST parsing and traversal, not Vue SFC block handling or Vite integration |