CodeVetter · Repo Unpack · Extension points
vuejs/core
Extensibility is designed in at four levels. Platform level: `createRenderer(options)` lets anyone build a custom renderer from the same core — `runtime-test` is the in-repo proof. Compile level: `nodeTransforms`/`directiveTransforms` are concatenated after the built-in presets, and compiler-dom/compiler-ssr are themselves just different preset compositions. App level: `app.use`/`app.component`/`app.directive`/`app.mixin` plus `app.config` hooks. Type level: declaration merging into `@vue/runtime-core` interfaces. Adding a package means adding a directory with a `buildOptions` block — `scripts/aliases.js` and `scripts/build.js` discover it automatically.
- `createRenderer<HostNode, HostElement>(options)` is the platform extension point; `createHydrationRenderer` is a separate export specifically so hydration stays tree-shakable, and both delegate to the same `baseCreateRenderer`.
- A renderer is defined entirely by the `RendererOptions` object; runtime-dom composes it as `extend({ patchProp }, nodeOps)`, and `packages/runtime-test` exists as a complete alternative host that renders plain JS objects for assertions.
- `baseCompile` appends `options.nodeTransforms` after the built-in preset and merges `options.directiveTransforms` over the built-in `on`/`bind`/`model` map, so third-party compiler plugins plug in without forking the preset.
- `createStructuralDirectiveTransform` in transform.ts is the factory for writing new structural directives in the style of v-if/v-for, and `traverseNode` is the hook point where each `nodeTransforms[i]` runs and may return an on-exit callback.
- compiler-dom's `DOMNodeTransforms` and `DOMDirectiveTransforms` are exported arrays/records, explicitly so compiler-ssr can reuse them to generate vnode fallback branches — the comment on `ignoreSideEffectTags` documents why it is kept out of that list.
- The app-level registries are `context.components`, `context.directives`, `context.provides`, `context.mixins` and the three WeakMap caches (`optionsCache`, `propsCache`, `emitsCache`) created by `createAppContext()`; `app.component`/`app.directive` are thin registrars over the first two with dev-time name validation.
- The plugin interface is `Plugin<Options> = FunctionPlugin | ObjectPlugin` with an `install(app, ...options)` signature; `app.onUnmount(cb)` gives plugins a teardown hook invoked through `callWithAsyncErrorHandling` with `ErrorCodes.APP_UNMOUNT_CLEANUP`.
- `AppConfig` exposes `errorHandler`, `warnHandler`, `globalProperties`, `optionMergeStrategies`, `compilerOptions` and `performance` as the supported runtime customisation surface, with `app.config` itself made non-replaceable via a warning setter.
- TypeScript extension is via declaration merging into named interfaces — runtime-dom itself does this for `RefUnwrapBailTypes`, `AllowedAttrs`, `GlobalComponents` and `GlobalDirectives`, which is the same mechanism router/store libraries use.
- `HydrationStrategyFactory` is an exported type and the four built-in strategies are plain factory functions, so a userland strategy can be passed to `defineAsyncComponent({ hydrate })` without touching core.
- `registerRuntimeCompiler(_compile)` is the documented seam for swapping the runtime compiler, and `isRuntimeOnly()` is derived from whether it has been called — that single boolean drives the dev-only `compilerOptions` deprecation warnings.
- `registerTS(loadTS)` and `invalidateTypeCache` are exported from compiler-sfc as the integration seam for bundlers to supply their own TypeScript instance and bust the type cache on HMR, with a hand-written error message when the module cannot be resolved.
- Adding a new workspace package requires no build wiring: `scripts/aliases.js` enumerates `packages/*` at runtime (skipping a `nonSrcPackages` denylist) and `scripts/build.js`/`rollup.config.js` read `packages-private` from disk to decide the package base directory.