'no-restricted-syntax': 'off'
}
},
+ // shared, may be used in any env
+ {
+ files: ['packages/shared/**'],
+ rules: {
+ 'no-restricted-globals': 'off'
+ }
+ },
// Packages targeting DOM
{
files: ['packages/{vue,runtime-dom}/**'],
__ESM_BUNDLER__: true,
__ESM_BROWSER__: false,
__NODE_JS__: true,
- __FEATURE_OPTIONS__: true,
+ __FEATURE_OPTIONS_API__: true,
__FEATURE_SUSPENSE__: true
},
coverageDirectory: 'coverage',
declare var __VERSION__: string
// Feature flags
-declare var __FEATURE_OPTIONS__: boolean
+declare var __FEATURE_OPTIONS_API__: boolean
+declare var __FEATURE_PROD_DEVTOOLS__: boolean
declare var __FEATURE_SUSPENSE__: boolean
import { warn } from './warning'
import { createVNode, cloneVNode, VNode } from './vnode'
import { RootHydrateFunction } from './hydration'
-import { initApp, appUnmounted } from './devtools'
+import { devtoolsInitApp, devtoolsUnmountApp } 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 and devtools
+ // internal, but we need to expose these for the server-renderer and devtools
_component: Component
_props: Data | null
_container: HostElement | null
// @private
readonly isNativeTag?: (tag: string) => boolean
- devtools: boolean
performance: boolean
optionMergeStrategies: Record<string, OptionMergeFunction>
globalProperties: Record<string, any>
}
export interface AppContext {
+ app: App // for devtools
config: AppConfig
mixins: ComponentOptions[]
components: Record<string, PublicAPIComponent>
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
export function createAppContext(): AppContext {
return {
+ app: null as any,
config: {
isNativeTag: NO,
- devtools: true,
performance: false,
globalProperties: {},
optionMergeStrategies: {},
let isMounted = false
- const app: App = {
+ const app: App = (context.app = {
_component: rootComponent as Component,
_props: rootProps,
_container: null,
},
mixin(mixin: ComponentOptions) {
- if (__FEATURE_OPTIONS__) {
+ if (__FEATURE_OPTIONS_API__) {
if (!context.mixins.includes(mixin)) {
context.mixins.push(mixin)
} else if (__DEV__) {
}
isMounted = true
app._container = rootContainer
+ // for devtools and telemetry
+ ;(rootContainer as any).__vue_app__ = app
- __DEV__ && initApp(app, version)
+ if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
+ devtoolsInitApp(app, version)
+ }
return vnode.component!.proxy
} else if (__DEV__) {
unmount() {
if (isMounted) {
render(null, app._container)
-
- __DEV__ && appUnmounted(app)
+ devtoolsUnmountApp(app)
} else if (__DEV__) {
warn(`Cannot unmount an app that is not mounted.`)
}
return app
}
- }
-
- context.__app = app
+ })
return app
}
markAttrsAccessed
} from './componentRenderUtils'
import { startMeasure, endMeasure } from './profiling'
-import { componentAdded } from './devtools'
+import { devtoolsComponentAdded } from './devtools'
export type Data = Record<string, unknown>
instance.root = parent ? parent.root : instance
instance.emit = emit.bind(null, instance)
- __DEV__ && componentAdded(instance)
+ if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
+ devtoolsComponentAdded(instance)
+ }
return instance
}
}
// support for 2.x options
- if (__FEATURE_OPTIONS__) {
+ if (__FEATURE_OPTIONS_API__) {
currentInstance = instance
applyOptions(instance, Component)
currentInstance = null
// apply mixin/extends props
let hasExtends = false
- if (__FEATURE_OPTIONS__ && !isFunction(comp)) {
+ if (__FEATURE_OPTIONS_API__ && !isFunction(comp)) {
if (comp.extends) {
hasExtends = true
extend(normalized, normalizeEmitsOptions(comp.extends))
// apply mixin/extends props
let hasExtends = false
- if (__FEATURE_OPTIONS__ && !isFunction(comp)) {
+ if (__FEATURE_OPTIONS_API__ && !isFunction(comp)) {
const extendProps = (raw: ComponentOptions) => {
const [props, keys] = normalizePropsOptions(raw)
extend(normalized, props)
$parent: i => i.parent && i.parent.proxy,
$root: i => i.root && i.root.proxy,
$emit: i => i.emit,
- $options: i => (__FEATURE_OPTIONS__ ? resolveMergedOptions(i) : i.type),
+ $options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type),
$forceUpdate: i => () => queueJob(i.update),
$nextTick: () => nextTick,
- $watch: __FEATURE_OPTIONS__ ? i => instanceWatch.bind(i) : NOOP
+ $watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP)
} as PublicPropertiesMap)
const enum AccessTypes {
types: Record<string, string | Symbol>
}
-enum DevtoolsHooks {
+const enum DevtoolsHooks {
APP_INIT = 'app:init',
APP_UNMOUNT = 'app:unmount',
COMPONENT_UPDATED = 'component:updated',
devtools = hook
}
-export function initApp(app: App, version: string) {
+export function devtoolsInitApp(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
+ Fragment,
+ Text,
+ Comment,
+ Static
})
}
-export function appUnmounted(app: App) {
+export function devtoolsUnmountApp(app: App) {
if (!devtools) return
devtools.emit(DevtoolsHooks.APP_UNMOUNT, app)
}
-export const componentAdded = createDevtoolsHook(DevtoolsHooks.COMPONENT_ADDED)
+export const devtoolsComponentAdded = /*#__PURE__*/ createDevtoolsHook(
+ DevtoolsHooks.COMPONENT_ADDED
+)
-export const componentUpdated = createDevtoolsHook(
+export const devtoolsComponentUpdated = /*#__PURE__*/ createDevtoolsHook(
DevtoolsHooks.COMPONENT_UPDATED
)
-export const componentRemoved = createDevtoolsHook(
+export const devtoolsComponentRemoved = /*#__PURE__*/ createDevtoolsHook(
DevtoolsHooks.COMPONENT_REMOVED
)
function createDevtoolsHook(hook: DevtoolsHooks) {
return (component: ComponentInternalInstance) => {
- if (!devtools || !component.appContext.__app) return
+ if (!devtools) return
devtools.emit(
hook,
- component.appContext.__app,
+ component.appContext.app,
component.uid,
component.parent ? component.parent.uid : undefined
)
--- /dev/null
+import { getGlobalThis } from '@vue/shared'
+
+/**
+ * This is only called in esm-bundler builds.
+ * It is called when a renderer is created, in `baseCreateRenderer` so that
+ * importing runtime-core is side-effects free.
+ *
+ * istanbul-ignore-next
+ */
+export function initFeatureFlags() {
+ let needWarn = false
+
+ if (typeof __FEATURE_OPTIONS_API__ !== 'boolean') {
+ needWarn = true
+ getGlobalThis().__VUE_OPTIONS_API__ = true
+ }
+
+ if (typeof __FEATURE_PROD_DEVTOOLS__ !== 'boolean') {
+ needWarn = true
+ getGlobalThis().__VUE_PROD_DEVTOOLS__ = false
+ }
+
+ if (__DEV__ && needWarn) {
+ console.warn(
+ `You are running the esm-bundler build of Vue. It is recommended to ` +
+ `configure your bundler to explicitly replace the following global ` +
+ `variables with boolean literals so that it can remove unnecessary code:\n\n` +
+ `- __VUE_OPTIONS_API__ (support for Options API, default: true)\n` +
+ `- __VUE_PROD_DEVTOOLS__ (enable devtools inspection in production, default: false)`
+ // TODO link to docs
+ )
+ }
+}
import { invokeDirectiveHook } from './directives'
import { startMeasure, endMeasure } from './profiling'
import { ComponentPublicInstance } from './componentProxy'
-import { componentRemoved, componentUpdated } from './devtools'
+import { devtoolsComponentRemoved, devtoolsComponentUpdated } from './devtools'
+import { initFeatureFlags } from './featureFlags'
export interface Renderer<HostElement = RendererElement> {
render: RootRenderFunction<HostElement>
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions
): any {
+ // compile-time feature flags check
+ if (__ESM_BUNDLER__ && !__TEST__) {
+ initFeatureFlags()
+ }
+
const {
insert: hostInsert,
remove: hostRemove,
invokeVNodeHook(vnodeHook!, parent, next!, vnode)
}, parentSuspense)
}
+
+ if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
+ devtoolsComponentUpdated(instance)
+ }
+
if (__DEV__) {
popWarningContext()
- componentUpdated(instance)
}
}
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
}
}
- __DEV__ && componentRemoved(instance)
+ if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
+ devtoolsComponentRemoved(instance)
+ }
}
const unmountChildren: UnmountChildrenFn = (
container.innerHTML = ''
const proxy = mount(container)
container.removeAttribute('v-cloak')
+ container.setAttribute('data-vue-app', '')
return proxy
}
const n = parseFloat(val)
return isNaN(n) ? val : n
}
+
+let _globalThis: any
+export const getGlobalThis = (): any => {
+ return (
+ _globalThis ||
+ (_globalThis =
+ typeof globalThis !== 'undefined'
+ ? globalThis
+ : typeof self !== 'undefined'
+ ? self
+ : typeof window !== 'undefined'
+ ? window
+ : typeof global !== 'undefined'
+ ? global
+ : {})
+ )
+}
-import { version, setDevtoolsHook } from '@vue/runtime-dom'
+import { setDevtoolsHook } from '@vue/runtime-dom'
+import { getGlobalThis } from '@vue/shared'
export function initDev() {
- const target: any = __BROWSER__ ? window : global
+ const target = getGlobalThis()
- target.__VUE__ = version
+ target.__VUE__ = true
setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__)
if (__BROWSER__) {
- // @ts-ignore `console.info` cannot be null error
- console[console.info ? 'info' : 'log'](
+ console.info(
`You are running a development build of Vue.\n` +
`Make sure to use the production build (*.prod.js) when deploying for production.`
)
__ESM_BROWSER__: isBrowserESMBuild,
// is targeting Node (SSR)?
__NODE_JS__: isNodeBuild,
- __FEATURE_OPTIONS__: true,
+
+ // feature flags
__FEATURE_SUSPENSE__: true,
+ __FEATURE_OPTIONS_API__: isBundlerESMBuild ? `__VUE_OPTIONS_API__` : true,
+ __FEATURE_PROD_DEVTOOLS__: isBundlerESMBuild
+ ? `__VUE_PROD_DEVTOOLS__`
+ : false,
...(isProduction && isBrowserBuild
? {
'context.onError(': `/*#__PURE__*/ context.onError(`,