@verter/native
Pre-Release
Verter is pre-release software. APIs may change between releases — see the API Stability document.
Native Node.js bindings via NAPI-RS. Provides high-performance SFC compilation for build tools and servers.
Installation
pnpm add @verter/nativeThe correct platform-specific binary is automatically selected at runtime via optional dependencies. No additional configuration is needed.
Platform Support
| Platform | Architecture | Package |
|---|---|---|
| Windows | x64 | @verter/native-win32-x64-msvc |
| Windows | ia32 | @verter/native-win32-ia32-msvc |
| Windows | ARM64 | @verter/native-win32-arm64-msvc |
| macOS | Universal | @verter/native-darwin-universal |
| macOS | x64 | @verter/native-darwin-x64 |
| macOS | ARM64 | @verter/native-darwin-arm64 |
| Linux | x64 (glibc) | @verter/native-linux-x64-gnu |
| Linux | x64 (musl) | @verter/native-linux-x64-musl |
| Linux | ARM64 (glibc) | @verter/native-linux-arm64-gnu |
| Linux | ARM64 (musl) | @verter/native-linux-arm64-musl |
API
processStyle(css, options)
Process a CSS style block: apply scoping, CSS modules, and v-bind() replacement.
Called by the unplugin after preprocessing SCSS/Less/Stylus to valid CSS. For plain CSS blocks, the Rust compiler handles this inline during compilation.
import { processStyle } from '@verter/native'
const result = processStyle(css, {
scopeId: 'a4f2eed6',
scoped: true,
})
// result.code — transformed CSS
// result.moduleClasses — CSS module mappings
// result.vBindVars — replaced v-bind() expressionsParameters:
css(string | Buffer) -- Valid CSS as a string or Buffer (UTF-8 bytes)options(ProcessStyleOptions) -- Processing options
Returns: ProcessStyleResult
VerterHost
In-memory virtual file host for multi-file compilation with caching and dependency tracking. This is the primary API for build tools that need to compile multiple .vue files with cross-file awareness.
import { VerterHost } from '@verter/native'
const host = new VerterHost({
devMode: false,
analysisLevel: 'full',
})host.resolve(rawId)
Resolve a raw module ID (e.g., a virtual file request like App.vue?vue&type=style&index=0&scoped&lang.css) into a canonical ID and node kind.
const resolved = host.resolve('App.vue?vue&type=style&index=0')
// resolved.canonicalId — 'App.vue'
// resolved.nodeKind — { kind: 'style', index: 0 }
// resolved.bundlerId — full virtual file ID for the bundler
// resolved.lspId — LSP-compatible URIReturns: HostResolvedId | null
host.upsert(request)
Register or update a file in the host. Handles parsing, caching, and change detection. Returns detailed information about what changed.
const result = host.upsert({
inputId: '/path/to/App.vue',
source: sfcSource, // string or Buffer
})
// result.canonicalId — resolved canonical ID
// result.changed — whether content actually changed
// result.sliceChanges — which blocks (script/template/style) changed
// result.externalSourceRequests — external src= attributes to resolve
// result.moduleReferences — import/require sites for dependency tracking
// result.diagnostics — parse-time diagnosticsHostUpsertRequest fields:
| Field | Type | Description |
|---|---|---|
inputId | string | File path or identifier |
source | string | Buffer | SFC source code (Buffer avoids UTF-16 decode) |
canonicalId | string? | Override canonical ID |
fileKind | string? | "vue", "non_sfc", "text", etc. |
aliases | string[]? | Additional IDs that resolve to this file |
Returns: HostUpdateResult
HostUpdateResult.moduleReferences is the shared dependency-tracking surface for non-IDE consumers. Each reference reports:
analyzability: "exact"when there is one literal specifieranalyzability: "finiteSet"when static analysis found a bounded candidate setanalyzability: "unknownDynamic"when the import is too dynamic to resolve safely
Only the exact and finite-set cases should feed dependency resolution. Unknown dynamic imports are intentionally left unresolved.
host.getVirtualFile(query)
Get a compiled virtual file from the host. Triggers compilation if needed.
const file = host.getVirtualFile({
rawId: 'App.vue',
compileProfile: {
isProduction: true,
ssr: false,
hmrStrategy: 'none',
sourceMap: true,
},
})
// file.code — compiled output
// file.sourceMap — source map JSON string
// file.lang — output language ('ts', 'js', 'css', etc.)
// file.diagnostics — compilation diagnosticsReturns: HostVirtualFileResponse
host.getPublicApi(canonicalId)
Get the public TypeScript surface for a Vue SFC.
const publicApi = host.getPublicApi('/path/to/App.vue')
// publicApi.code — public `.vue.ts` module surface
// publicApi.sourceMap — source map JSON stringFor provider and IDE consumers, importing App.vue resolves through the public .vue.ts surface. The internal IDE TSX/JSX virtual filename is not part of the public API contract.
Returns: { code: string; sourceMap?: string } | null
host.getIde(canonicalId, profile?)
Get the IDE representation of a file for type checking. Used by the LSP and provider integration.
const ide = host.getIde('/path/to/App.vue', {
target: 'ide',
})
// ide.code — valid TSX or JSX code
// ide.sourceMap — source map JSON string
// ide.isJsx — true for JSX, false for TSXThe IDE virtual filename used behind this API is internal. Consumers should rely on the returned code and source map, not on a specific virtual suffix such as .vue.tsx.
Returns: HostIdeResponse | null
host.applyBlockOverrides(request)
Apply preprocessed block overrides (template, script, style, or custom blocks). Used after an external preprocessor (Pug, SCSS, Less, Stylus, etc.) has transformed block content.
const result = host.applyBlockOverrides({
canonicalId: '/path/to/App.vue',
overrides: [
{ blockType: 'style', index: 0, code: processedCss },
],
})Returns: HostUpdateResult
host.listVirtualFiles(canonicalId)
List all virtual file nodes for a given canonical ID.
const nodes = host.listVirtualFiles('/path/to/App.vue')
// [{ kind: 'main' }, { kind: 'script' }, { kind: 'style', index: 0 }]Returns: HostVirtualNodeKind[]
host.remove(canonicalOrAlias)
Remove a file from the host and invalidate its cache.
const result = host.remove('/path/to/App.vue')
// result.canonicalId — the canonical ID that was removedReturns: HostRemoveResult | null
host.getAnalysis(canonicalOrAlias)
Returns the static analysis snapshot for a file as a JSON string, or null if the file does not exist. When analysisLevel is not "full", computes analysis on demand.
const analysisJson = host.getAnalysis('/path/to/App.vue')
if (analysisJson) {
const analysis = JSON.parse(analysisJson)
}Returns: string | null
host.setImportDependencies(canonicalOrAlias, resolvedDeps)
Sets the resolved import dependencies for a file, enabling Tier 2/3 smart invalidation (cross-file change tracking).
host.setImportDependencies('/path/to/App.vue', [
'/path/to/types.ts',
'/path/to/composables.ts',
])Call this after you resolve moduleReferences from upsert(). The host does not guess unresolved dynamic imports for you.
host.collectResolvableModuleReferenceSpecifiers(moduleReferences)
Collect the exact and finite candidate specifiers from HostUpdateResult.moduleReferences, preserving encounter order and skipping unknownDynamic entries.
const { moduleReferences } = host.upsert({
inputId: '/path/to/App.vue',
source: sfcSource,
})
const candidates = host.collectResolvableModuleReferenceSpecifiers(moduleReferences)
// ['vue', './foo', './bar', './bar/index']Use this when you want the bundler or another resolver to handle candidate lookup. This is the contract used by @verter/unplugin: delegate only exact/finite candidates, leave unknown dynamic imports unresolved, then pass the resolved canonical IDs back through setImportDependencies().
Returns: string[]
host.resolveKnownModuleReferenceDependencies(ownerCanonicalId, moduleReferences, knownIds, extensions?)
Resolve exact and finite moduleReferences against an explicit in-memory file set, without reading from disk.
const knownIds = [
'/src/App.vue',
'/src/composables/useCount.ts',
'/src/types.ts',
]
const resolvedDeps = host.resolveKnownModuleReferenceDependencies(
'/src/App.vue',
moduleReferences,
knownIds,
['.ts', '.tsx', '.js', '.jsx', '.vue', '/index.ts'],
)
host.setImportDependencies('/src/App.vue', resolvedDeps)This is the shared non-IDE resolver path used by the playground. Resolution is restricted to the provided knownIds plus the caller-supplied extension order. The helper remains disk-free and skips every unknownDynamic import.
Parameters:
ownerCanonicalId(string) — canonical ID of the importing filemoduleReferences(HostModuleReference[]) —upsert()outputknownIds(string[]) — explicit file map or pre-known canonical IDsextensions(string[]?) — extension probe order for relative candidates
Returns: string[]
Types
ProcessStyleOptions
interface ProcessStyleOptions {
/** Scope ID string (e.g., "a4f2eed6") */
scopeId: string
/** Whether this style block is scoped */
scoped?: boolean
/** Whether this is a CSS module block */
isModule?: boolean
/** Custom module name (default: "$style") */
moduleName?: string
/** Source filename for source map generation */
filename?: string
/** Whether to generate source maps */
sourcemap?: boolean
}ProcessStyleResult
interface ProcessStyleResult {
/** Transformed CSS code */
code: string
/** Source map as JSON string (if sourcemap was requested) */
sourceMap?: string
/** CSS module class mappings: [original, hashed][] */
moduleClasses: [string, string][]
/** Resolved CSS module name */
moduleName?: string
/** v-bind() expressions found and replaced */
vBindVars: ProcessStyleVBind[]
}HostConfig
interface HostConfig {
/** Enable development mode */
devMode?: boolean
/** Error handling policy */
compileErrorPolicy?: 'strict' | 'strictError' | 'devServeLastKnownGood'
/** LSP URI scheme */
lspScheme?: string
/** Maximum compile profiles cached per file */
maxProfilesPerFile?: number
/** File extensions to try during resolution */
resolveExtensions?: string[]
/** Static analysis level during upsert(). Default: "full" */
analysisLevel?: 'full' | 'essential' | 'none'
}HostCompileProfile
Controls how a file is compiled. Different profiles produce different outputs (e.g., dev vs. prod, SSR vs. client).
interface HostCompileProfile {
/** Override filename for the compilation */
filename?: string
/** Production mode optimizations */
isProduction?: boolean
/** Server-side rendering mode */
ssr?: boolean
/** HMR strategy: "none", "vite", or "webpack" */
hmrStrategy?: 'none' | 'vite' | 'webpack'
/** Component scope ID */
componentId?: string
/** Custom template delimiters */
delimiters?: [string, string]
/** Custom element tag names */
customElements?: string[]
/** Preserve HTML comments in output */
comments?: boolean
/** Custom Vue runtime module name */
runtimeModuleName?: string
/** Custom types module name */
typesModuleName?: string
/** Force Vapor mode output */
forceVapor?: boolean
/** Force JavaScript output (strip TypeScript) */
forceJs?: boolean
/** Generate source maps */
sourceMap?: boolean
/** Compilation target preset */
target?: 'bundler' | 'ide' | 'analysis'
}HostUpdateResult
interface HostUpdateResult {
canonicalId: string
changed: boolean
sliceChanges: HostSliceChanges
changedVirtualNodes: HostVirtualNodeKind[]
removedVirtualNodes: HostVirtualNodeKind[]
changedVirtualIds: string[]
removedVirtualIds: string[]
changedLspIds: string[]
removedLspIds: string[]
diagnostics: HostDiagnosticsSnapshot
externalSourceRequests: HostExternalSourceRequest[]
importSpecifiers: HostScriptImportInfo[]
moduleReferences: HostModuleReference[]
preprocessorRequests: HostPreprocessorRequest[]
parseDurationMs: number
}HostVirtualFileResponse
interface HostVirtualFileResponse {
id: string
code: string
sourceMap?: string
lang?: string
stale: boolean
diagnostics: HostDiagnosticsSnapshot
meta: HostVirtualMeta
}HostDiagnosticsSnapshot
interface HostDiagnosticsSnapshot {
diagnostics: HostDiagnostic[]
hasErrors: boolean
}
interface HostDiagnostic {
severity: 'error' | 'warning' | 'info'
code: string
message: string
spanStart?: number
spanEnd?: number
}Input Encoding
The native binding always receives bytes (Buffer) internally. The JS wrapper accepts both string and Buffer for convenience -- strings are automatically converted to UTF-8 Buffers before crossing the FFI boundary.
For best performance when reading files from disk, pass Buffer directly to avoid the intermediate UTF-16 string decode:
import { readFileSync } from 'fs'
// Optimal: pass Buffer directly (no string decode)
const source = readFileSync('App.vue')
host.upsert({ inputId: 'App.vue', source })
// Also works: string is converted to Buffer internally
const sourceStr = readFileSync('App.vue', 'utf-8')
host.upsert({ inputId: 'App.vue', source: sourceStr })