Developer tools after migration
The migration setup extends the repository's existing format and lint scripts, rather than accepting a green command that skips VibeLang.
Build entry points
A batched migration follows renamed sources in owned tool configurations,
even when the package inventories only src. The entry arrays and maps used
by tsdown or tsup, rolldown's input, and Vite's build.lib.entry
keep pointing at the same modules. This applies to TypeScript and JavaScript
*.config.* files. Protected test configurations and fixtures stay as written.
Resumed batches retain earlier filename edits, and reverting a module restores
its original configuration entry.
Prettier and Oxfmt
Install vibelang and configure Prettier's plugins with
"vibelang/prettier" (or pass --plugin vibelang/prettier). The plugin infers
.vibe and delegates the entire file to the shipped dprint process plugin,
the same formatter used by vibelang-format --write and
vibelang-format --check. Prettier's singleQuote and printWidth decide
the quote style and line width; its other layout options do not alter the
result.
For editor integrations, formatSource(text, filePath) from vibelang/format formats the supplied buffer even when its path is ignored or under node_modules. The full path selects the repository's style; the caller selects which buffers to format. The Prettier plugin uses the same behavior and reports syntax errors in unsaved input. File-scanning commands apply their ignore rules when selecting files.
vibelang-format reads the same two settings from the repository's formatter
configuration, so .vibe files follow the style of its .ts files. It uses
the nearest configuration from the file's directory up to the repository's
root: .oxfmtrc.json or .oxfmtrc.jsonc (Oxfmt), .prettierrc,
.prettierrc.json or the prettier key of package.json (Prettier), or
dprint.json (its typescript quoteStyle and lineWidth). A setting the
configuration leaves out takes that formatter's default: width 100 for Oxfmt,
80 for Prettier, 120 for dprint, and double quotes. With no configuration,
files get double quotes and width 120. A Prettier configuration in another
form, such as .prettierrc.yaml or prettier.config.js, stops
vibelang-format with an error naming the file: format through Prettier with
vibelang/prettier, which reads every form. dprint 0.64.3 is installed
with the compiler; the pinned TypeScript Wasm plugin is downloaded and cached
on first use.
Oxfmt's unsupported features
explicitly exclude Prettier plugins. Its
CLI source
has no external formatter option. Internal N-API external-service callbacks
are implementation details, not a user language-plugin registration API.
Migration therefore appends vibelang-format --check or --write to
Oxfmt and Vite+ format scripts. The same check extends Vite+ staged hooks.
Raw oxfmt file.vibe remains unsupported; use the migrated script.
Existing dprint JSON configs gain the shipped plugin and .vibe includes.
Oxlint
Oxlint's JS plugin API explicitly excludes custom formats and parsers. Its partial loader dispatches only Vue, Astro, and Svelte script regions. VibeLang is a language, not a container of JavaScript script tags, so that mechanism cannot parse it.
Migration keeps upstream oxlint. For a package's existing Oxlint/Vite+
lint script, setup adds one pass for each oxlint invocation in it,
including one in a subshell such as (oxlint --disable-nested-config), over
the same paths with the configuration named by -c or --config. ESLint
checks .vibe through vibelang/eslint. Each generated pass retains its
invocation's --deny-warnings or --max-warnings 0 policy. Running setup
again refreshes its generated configurations and passes.
Setup adds --no-error-on-unmatched-pattern to the native invocation so a
directory containing only .vibe files still reaches ESLint.
It also follows local JavaScript and TypeScript runners launched by package
scripts, including their relative imports. In runners that invoke Oxlint it
extends TypeScript extension filters to .vibe, keeps directory and package
filters, and excludes .d.vibe.ts declaration sidecars. Unsupported filter
shapes produce a setup warning naming the runner.
File-list runners invoke the generated .vibelang/oxlint.mjs adapter.
Upstream Oxlint checks their TypeScript files; ESLint checks their VibeLang
files with the repository's JS plugins on the original source tree. The
generated configuration retains rule names, options, severities and directory
overrides, including overrides such as packages/gui/** that do not name an
extension. It reads the repository's configuration on each invocation. JSON
output merges both passes in Oxlint's diagnostic shape, with the original
.vibe filename and line. Warn rules remain warnings; a runner that rejects
every problem, or --deny-warnings, rejects them too.
Existing oxlint-disable, oxlint-disable-line,
oxlint-disable-next-line and oxlint-enable comments keep their scope.
The bridge also recognizes ESLint directives and the native core/TypeScript
rule aliases they name. It adapts parsed comments without changing the user
file, string literals or source locations; unused directives remain quiet,
as in Oxlint by default.
The ESLint configuration carries applicable core, TypeScript and JS plugin
rules. It also supplies oxc/no-accumulating-spread, checked against upstream
Oxlint's reducer and loop behavior. A file-list runner fails with the names of
enabled rules that have no ESLint counterpart, rather than silently skipping
them. When the default correctness category is enabled, typescript-eslint's
recommended rules provide the existing ESLint fallback; turning that
category off disables that fallback. Other native Oxlint rules are not
automatically ported. Rules requiring project type information remain with
vibelang lint (VIP-38).
The mapped no-unused-vars rule counts typeof reads as Oxlint does,
including erased declare const symbol brands, while retaining configured
options and reporting unused declarations.
Setup installs neither an Oxlint patch nor a fork. No projection hook or platform-specific binary is needed beyond the existing VibeLang compiler.
ast-grep
vibelang-ast-grep <output> compiles the canonical
editors/tree-sitter-vibelang/src parser and scanner into a shared library.
The compiler npm release includes these sources; a C compiler is required.
Migration registers VibeLang in each sgconfig.yml, with the symbol
tree_sitter_vibe, extension vibe, and a generated library beside the
config. It clones TypeScript/TSX rules with VibeLang language selectors and
unique IDs: registration alone would not apply existing rules.
The library is ignored and rebuilt by the repository's postinstall command,
so a fresh checkout builds for its own machine. Linux and macOS use the same
extensionless output path. Windows developers use WSL.
For ast-grep 0.50+, custom language loading
also requires --custom-languages allow or project trust; earlier versions
load registered languages directly. Existing pinned versions are retained.
Migration recognizes TypeScript/TSX aliases without case sensitivity and translates TS file selectors and ignores to .vibe, preserving directory scope.
Bundlers inside unchanged tests
A test may call Rolldown's API or start Vite's bundled config loader on a
fixture that imports the package's own sources. Runtime register hooks do
not configure those bundlers. When the package's existing build or
prepare script already compiles a project to individual JavaScript files,
give migration's upgrade-effect command --emitted-project with that
repository-relative project path. Repeat it for more than one project.
Setup rewrites runtime imports in the package's product sources to exact
private #vibelang/emitted/… entries. Each entry has a vibelang-source
condition pointing at the source, a types condition for TypeScript, and
a default condition pointing at the compiler's JavaScript output. Setup
enables vibelang-source in the package's compiler projects' customConditions,
so a cold compiler build reads sources before declaration sidecars exist.
Node, Rolldown and Vite consume the emitted JavaScript through default.
Setup records every selected source, output and alias in
setup.json's wired.emittedImports.modules. It uses exact entries instead
of wildcard selectors and leaves tests, fixtures, test scripts, type-only
imports and consumer exports intact. It adds no build step: the existing
build must produce those outputs before the unchanged consumers run.
A project that does not emit JavaScript, is outside the selected packages,
or is not already compiled by build or prepare is refused.
For published packages, setup emits declarations before and after this wiring and requires identical bytes. An alias that would leak into the published declarations causes setup to restore every planned edit and report the changed declaration paths. The migration's later declaration and test gates still apply.
Metro, Babel and the React Compiler
Tools that read one file at a time, such as Metro, Babel and Oxc, cannot parse
a .vibe module. Three entry points hand them the module's JavaScript from
the project's compile and move the parsed syntax tree back to .vibe
positions, so diagnostics and source maps name .vibe lines:
vibelang/metro:withVibeLang(config)around the config thatmetro.config.jsexports. Metro resolves.vibemodules, and the app's own Babel transformer receives their JavaScript. See React Native and Metro.vibelang/babel: a Babel preset, listed last, for a tool that runs Babel on a file itself, such as babel-jest or a React Compiler coverage test.toolArguments(filename, text, options)fromvibelang/emit: the arguments for atransform(filename, text, options)API such as oxc-transform-react.
The React Compiler sees JavaScript. On a .vibe module's JavaScript it
reports the same diagnostics and writes the same output as on the module's
.ts twin.
Migration setup wires each package that reaches a migrated module: it wraps
the metro.config export in withVibeLang, adds vibelang/babel to the
Babel presets, and adds .vibe to the include of each
@vitejs/plugin-react call that turns the React Compiler on, whose default
filter /\.[tj]sx?$/ skips .vibe modules. Under pnpm or Bun it writes
patches/oxc-transform-react@<version>.patch and a patchedDependencies
entry, so an unchanged test that calls oxc-transform-react's transform on a
.vibe module's text checks the module's JavaScript. Under npm or Yarn,
setup warns instead.
vibelang declares Metro and Babel as optional peer dependencies: an app
without them installs nothing extra.