Skip to content
LogoLogo

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 that metro.config.js exports. Metro resolves .vibe modules, 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) from vibelang/emit: the arguments for a transform(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.