*/
transformAssetUrls?: AssetURLOptions | AssetURLTagConfig | boolean
}
-
+
interface PreProcessor {
render(
source: string,
render(h(Comp, { 'foo-bar': 3, bar: 3, baz: 4, barBaz: 5 }), root)
expect(proxy.fooBar).toBe(3)
expect(proxy.barBaz).toBe(5)
- expect(props).toEqual({ fooBar: 3,barBaz: 5 })
+ expect(props).toEqual({ fooBar: 3, barBaz: 5 })
expect(attrs).toEqual({ bar: 3, baz: 4 })
render(h(Comp, { qux: 5 }), root)
})
it('should warn render ssr slot', () => {
- renderSlot({ default: (a, b, c) => [h('child')] }, 'default')
+ renderSlot({ default: (_a, _b, _c) => [h('child')] }, 'default')
expect('SSR-optimized slot function detected').toHaveBeenWarned()
})
})
import { warn } from './warning'
import { createVNode, cloneVNode, VNode } from './vnode'
import { RootHydrateFunction } from './hydration'
+import { initApp, appUnmounted } from './devtools'
import { version } from '.'
export interface App<HostElement = any> {
unmount(rootContainer: HostElement | string): void
provide<T>(key: InjectionKey<T> | string, value: T): this
- // internal. We need to expose these for the server-renderer
+ // internal. We need to expose these for the server-renderer and devtools
_component: Component
_props: Data | null
_container: HostElement | null
directives: Record<string, Directive>
provides: Record<string | symbol, any>
reload?: () => void // HMR only
+
+ // internal for devtools
+ __app?: App
}
type PluginInstallFunction = (app: App, ...options: any[]) => any
}
isMounted = true
app._container = rootContainer
+
+ __DEV__ && initApp(app, version)
+
return vnode.component!.proxy
} else if (__DEV__) {
warn(
unmount() {
if (isMounted) {
render(null, app._container)
+
+ __DEV__ && appUnmounted(app)
} else if (__DEV__) {
warn(`Cannot unmount an app that is not mounted.`)
}
}
}
+ context.__app = app
+
return app
}
}
markAttrsAccessed
} from './componentRenderUtils'
import { startMeasure, endMeasure } from './profiling'
+import { componentAdded } from './devtools'
export type Data = { [key: string]: unknown }
}
instance.root = parent ? parent.root : instance
instance.emit = emit.bind(null, instance)
+
+ __DEV__ && componentAdded(instance)
+
return instance
}
--- /dev/null
+import { App } from './apiCreateApp'
+import { Fragment, Text, Comment, Static } from './vnode'
+import { ComponentInternalInstance } from './component'
+
+export interface AppRecord {
+ id: number
+ app: App
+ version: string
+ types: { [key: string]: string | Symbol }
+}
+
+enum DevtoolsHooks {
+ APP_INIT = 'app:init',
+ APP_UNMOUNT = 'app:unmount',
+ COMPONENT_UPDATED = 'component:updated',
+ COMPONENT_ADDED = 'component:added',
+ COMPONENT_REMOVED = 'component:removed'
+}
+
+export interface DevtoolsHook {
+ emit: (event: string, ...payload: any[]) => void
+ on: (event: string, handler: Function) => void
+ once: (event: string, handler: Function) => void
+ off: (event: string, handler: Function) => void
+ appRecords: AppRecord[]
+}
+
+export let devtools: DevtoolsHook
+
+export function setDevtoolsHook(hook: DevtoolsHook) {
+ devtools = hook
+}
+
+export function initApp(app: App, version: string) {
+ // TODO queue if devtools is undefined
+ if (!devtools) return
+ devtools.emit(DevtoolsHooks.APP_INIT, app, version, {
+ Fragment: Fragment,
+ Text: Text,
+ Comment: Comment,
+ Static: Static
+ })
+}
+
+export function appUnmounted(app: App) {
+ if (!devtools) return
+ devtools.emit(DevtoolsHooks.APP_UNMOUNT, app)
+}
+
+export function componentAdded(component: ComponentInternalInstance) {
+ if (!devtools || !component.appContext.__app) return
+ devtools.emit(
+ DevtoolsHooks.COMPONENT_ADDED,
+ component.appContext.__app,
+ component.uid,
+ component.parent ? component.parent.uid : undefined
+ )
+}
+
+export function componentUpdated(component: ComponentInternalInstance) {
+ if (!devtools || !component.appContext.__app) return
+ devtools.emit(
+ DevtoolsHooks.COMPONENT_UPDATED,
+ component.appContext.__app,
+ component.uid,
+ component.parent ? component.parent.uid : undefined
+ )
+}
+
+export function componentRemoved(component: ComponentInternalInstance) {
+ if (!devtools || !component.appContext.__app) return
+ devtools.emit(
+ DevtoolsHooks.COMPONENT_REMOVED,
+ component.appContext.__app,
+ component.uid,
+ component.parent ? component.parent.uid : undefined
+ )
+}
getTransitionRawChildren
} from './components/BaseTransition'
-// Types -----------------------------------------------------------------------
+// For devtools
+export { devtools, setDevtoolsHook } from './devtools'
+
+// Types -------------------------------------------------------------------------
import { VNode } from './vnode'
import { ComponentInternalInstance } from './component'
import { invokeDirectiveHook } from './directives'
import { startMeasure, endMeasure } from './profiling'
import { ComponentPublicInstance } from './componentProxy'
+import { componentRemoved, componentUpdated } from './devtools'
export interface Renderer<HostElement = RendererElement> {
render: RootRenderFunction<HostElement>
}
if (__DEV__) {
popWarningContext()
+ componentUpdated(instance)
}
}
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
parentSuspense.resolve()
}
}
+
+ __DEV__ && componentRemoved(instance)
}
const unmountChildren: UnmountChildrenFn = (
expect(
await renderToStream(
createApp(
- defineComponent((props: {}) => {
+ defineComponent(() => {
const msg = ref('hello')
return () => h('div', msg.value)
})
{ msg: 'hello' },
{
// optimized slot using string push
- default: ({ msg }: any, push: any, p: any) => {
+ default: ({ msg }: any, push: any) => {
push(`<span>${msg}</span>`)
},
// important to avoid slots being normalized
--- /dev/null
+import { version, setDevtoolsHook } from '@vue/runtime-dom'
+
+export function initDev() {
+ const target: any = __BROWSER__ ? window : global
+
+ target.__VUE__ = version
+ setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__)
+
+ if (__BROWSER__) {
+ // @ts-ignore `console.info` cannot be null error
+ console[console.info ? 'info' : 'log'](
+ `You are running a development build of Vue.\n` +
+ `Make sure to use the production build (*.prod.js) when deploying for production.`
+ )
+ }
+}
+++ /dev/null
-if (__BROWSER__ && __DEV__) {
- // @ts-ignore `console.info` cannot be null error
- console[console.info ? 'info' : 'log'](
- `You are running a development build of Vue.\n` +
- `Make sure to use the production build (*.prod.js) when deploying for production.`
- )
-}
// This entry is the "full-build" that includes both the runtime
// and the compiler, and supports on-the-fly compilation of the template option.
-import './devCheck'
+import { initDev } from './dev'
import { compile, CompilerOptions, CompilerError } from '@vue/compiler-dom'
import { registerRuntimeCompiler, RenderFunction, warn } from '@vue/runtime-dom'
import * as runtimeDom from '@vue/runtime-dom'
import { isString, NOOP, generateCodeFrame, extend } from '@vue/shared'
+__DEV__ && initDev()
+
const compileCache: Record<string, RenderFunction> = Object.create(null)
function compileToFunction(
// This entry exports the runtime only, and is built as
// `dist/vue.esm-bundler.js` which is used by default for bundlers.
-import './devCheck'
+import { initDev } from './dev'
import { warn } from '@vue/runtime-dom'
+__DEV__ && initDev()
+
export * from '@vue/runtime-dom'
export const compile = () => {
defineComponent({
emits: {
click: (n: number) => typeof n === 'number',
- input: (b: string) => null
+ input: (b: string) => b.length > 1
},
setup(props, { emit }) {
emit('click', 1)
expectType<HTMLElement>(unref(arg))
// ref inner type should be unwrapped
+ // eslint-disable-next-line no-restricted-globals
const nestedRef = ref({ foo: ref(document.createElement('DIV')) })
expectType<Ref<{ foo: HTMLElement }>>(nestedRef)
expectType<{ foo: HTMLElement }>(nestedRef.value)
}
+// eslint-disable-next-line no-restricted-globals
const el = document.createElement('DIV')
bailType(el)